diff --git a/CHANGELOG.md b/CHANGELOG.md index f752fec3..dc8853ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Don't generate fields that represent compositions of aspects within mutation types that represent services - Moved the `GraphQLAdapter` module to the root directory (`index.js`), simplifying the import process and reducing the required typing. +- Disabled conjunction on the same field for the following operators: + + `eq` (Equal) + + `gt` (Greater Than) + + `ge` (Greater Than or Equal) + + `le` (Less Than or Equal) + + `lt` (Less Than) + + `startswith` + + `endswith` ### Fixed diff --git a/lib/constants.js b/lib/constants.js index 11ee32d9..cc41d5c7 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -26,9 +26,22 @@ const STRING_OPERATIONS = { contains: 'contains' } +const OPERATOR_CONJUNCTION_SUPPORT = { + [RELATIONAL_OPERATORS.eq]: false, + [RELATIONAL_OPERATORS.ne]: true, + [RELATIONAL_OPERATORS.gt]: false, + [RELATIONAL_OPERATORS.ge]: false, + [RELATIONAL_OPERATORS.le]: false, + [RELATIONAL_OPERATORS.lt]: false, + [STRING_OPERATIONS.startswith]: false, + [STRING_OPERATIONS.endswith]: false, + [STRING_OPERATIONS.contains]: true +} + module.exports = { CONNECTION_FIELDS, ARGS, RELATIONAL_OPERATORS, - STRING_OPERATIONS + STRING_OPERATIONS, + OPERATOR_CONJUNCTION_SUPPORT } diff --git a/lib/resolvers/parse/ast/fromObject.js b/lib/resolvers/parse/ast/fromObject.js index 02a8a809..cf3d0fac 100644 --- a/lib/resolvers/parse/ast/fromObject.js +++ b/lib/resolvers/parse/ast/fromObject.js @@ -32,13 +32,20 @@ const _arrayToListValue = array => ({ const _variableToValue = variable => { if (Array.isArray(variable)) { return _arrayToListValue(variable) - } else if (isPlainObject(variable)) { + } + + if (isPlainObject(variable)) { return _objectToObjectValue(variable) - } else if (variable === null) { + } + + if (variable === null) { return _nullValue - } else if (variable === undefined) { + } + + if (variable === undefined) { return undefined } + return _valueToGenericScalarValue(variable) } diff --git a/lib/resolvers/parse/ast2cqn/where.js b/lib/resolvers/parse/ast2cqn/where.js index e94d3c4a..1bfe5de4 100644 --- a/lib/resolvers/parse/ast2cqn/where.js +++ b/lib/resolvers/parse/ast2cqn/where.js @@ -24,7 +24,6 @@ const _to_xpr = (ref, gqlOperator, value) => { const val = { val: value } if (STRING_OPERATIONS[gqlOperator]) return [{ func: cdsOperator, args: [ref, val] }] - return [ref, cdsOperator, val] } diff --git a/lib/schema/args/filter.js b/lib/schema/args/filter.js index d386176e..921220a1 100644 --- a/lib/schema/args/filter.js +++ b/lib/schema/args/filter.js @@ -10,7 +10,7 @@ const { const { gqlName } = require('../../utils') const { hasScalarFields, shouldElementBeIgnored } = require('../util') const { cdsToGraphQLScalarType } = require('../types/scalar') -const { RELATIONAL_OPERATORS, STRING_OPERATIONS } = require('../../constants') +const { RELATIONAL_OPERATORS, STRING_OPERATIONS, OPERATOR_CONJUNCTION_SUPPORT } = require('../../constants') const { GraphQLBinary, GraphQLDate, @@ -83,10 +83,14 @@ module.exports = cache => { const _generateFilterType = (gqlType, operations) => { const filterName = gqlType.name + '_filter' - const cacheFilterType = cache.get(filterName) - if (cacheFilterType) return cacheFilterType + const cachedFilterType = cache.get(filterName) + if (cachedFilterType) return cachedFilterType - const fields = Object.fromEntries(operations.map(op => [[op], { type: new GraphQLList(gqlType) }])) + const ops = operations.map(op => [ + [op], + { type: OPERATOR_CONJUNCTION_SUPPORT[op] ? new GraphQLList(gqlType) : gqlType } + ]) + const fields = Object.fromEntries(ops) const newFilterType = new GraphQLInputObjectType({ name: filterName, fields }) cache.set(filterName, newFilterType) diff --git a/test/schemas/bookshop-graphql.gql b/test/schemas/bookshop-graphql.gql index d7de93e6..b30eabca 100644 --- a/test/schemas/bookshop-graphql.gql +++ b/test/schemas/bookshop-graphql.gql @@ -635,7 +635,7 @@ The `Binary` scalar type represents binary values as `base64url` encoded strings scalar Binary input Binary_filter { - eq: [Binary] + eq: Binary ne: [Binary] } @@ -1277,11 +1277,11 @@ The `Date` scalar type represents date values as strings in the ISO 8601 format scalar Date input Date_filter { - eq: [Date] - ge: [Date] - gt: [Date] - le: [Date] - lt: [Date] + eq: Date + ge: Date + gt: Date + le: Date + lt: Date ne: [Date] } @@ -1291,11 +1291,11 @@ The `Decimal` scalar type represents exact signed decimal values. Decimal repres scalar Decimal input Decimal_filter { - eq: [Decimal] - ge: [Decimal] - gt: [Decimal] - le: [Decimal] - lt: [Decimal] + eq: Decimal + ge: Decimal + gt: Decimal + le: Decimal + lt: Decimal ne: [Decimal] } @@ -1305,20 +1305,20 @@ The `Int16` scalar type represents 16-bit non-fractional signed whole numeric va scalar Int16 input Int16_filter { - eq: [Int16] - ge: [Int16] - gt: [Int16] - le: [Int16] - lt: [Int16] + eq: Int16 + ge: Int16 + gt: Int16 + le: Int16 + lt: Int16 ne: [Int16] } input Int_filter { - eq: [Int] - ge: [Int] - gt: [Int] - le: [Int] - lt: [Int] + eq: Int + ge: Int + gt: Int + le: Int + lt: Int ne: [Int] } @@ -1339,14 +1339,14 @@ enum SortDirection { input String_filter { contains: [String] - endswith: [String] - eq: [String] - ge: [String] - gt: [String] - le: [String] - lt: [String] + endswith: String + eq: String + ge: String + gt: String + le: String + lt: String ne: [String] - startswith: [String] + startswith: String } """ @@ -1355,10 +1355,10 @@ The `Timestamp` scalar type represents timestamp values as strings in the ISO 86 scalar Timestamp input Timestamp_filter { - eq: [Timestamp] - ge: [Timestamp] - gt: [Timestamp] - le: [Timestamp] - lt: [Timestamp] + eq: Timestamp + ge: Timestamp + gt: Timestamp + le: Timestamp + lt: Timestamp ne: [Timestamp] } diff --git a/test/schemas/edge-cases/field-named-localized.gql b/test/schemas/edge-cases/field-named-localized.gql index 7edbea38..884aea15 100644 --- a/test/schemas/edge-cases/field-named-localized.gql +++ b/test/schemas/edge-cases/field-named-localized.gql @@ -114,11 +114,11 @@ input FieldNamedLocalizedService_localized_orderBy { } input Int_filter { - eq: [Int] - ge: [Int] - gt: [Int] - le: [Int] - lt: [Int] + eq: Int + ge: Int + gt: Int + le: Int + lt: Int ne: [Int] } @@ -137,12 +137,12 @@ enum SortDirection { input String_filter { contains: [String] - endswith: [String] - eq: [String] - ge: [String] - gt: [String] - le: [String] - lt: [String] + endswith: String + eq: String + ge: String + gt: String + le: String + lt: String ne: [String] - startswith: [String] + startswith: String } diff --git a/test/schemas/model-structure/composition-of-aspect.gql b/test/schemas/model-structure/composition-of-aspect.gql index a6b68cbe..73ff916b 100644 --- a/test/schemas/model-structure/composition-of-aspect.gql +++ b/test/schemas/model-structure/composition-of-aspect.gql @@ -107,11 +107,11 @@ type CompositionOfAspectService_input { } input ID_filter { - eq: [ID] - ge: [ID] - gt: [ID] - le: [ID] - lt: [ID] + eq: ID + ge: ID + gt: ID + le: ID + lt: ID ne: [ID] } diff --git a/test/schemas/types.gql b/test/schemas/types.gql index fe8f8fae..ef7c447e 100644 --- a/test/schemas/types.gql +++ b/test/schemas/types.gql @@ -4,12 +4,12 @@ The `Binary` scalar type represents binary values as `base64url` encoded strings scalar Binary input Binary_filter { - eq: [Binary] + eq: Binary ne: [Binary] } input Boolean_filter { - eq: [Boolean] + eq: Boolean ne: [Boolean] } @@ -24,20 +24,20 @@ The `DateTime` scalar type represents datetime values as strings in the ISO 8601 scalar DateTime input DateTime_filter { - eq: [DateTime] - ge: [DateTime] - gt: [DateTime] - le: [DateTime] - lt: [DateTime] + eq: DateTime + ge: DateTime + gt: DateTime + le: DateTime + lt: DateTime ne: [DateTime] } input Date_filter { - eq: [Date] - ge: [Date] - gt: [Date] - le: [Date] - lt: [Date] + eq: Date + ge: Date + gt: Date + le: Date + lt: Date ne: [Date] } @@ -47,29 +47,29 @@ The `Decimal` scalar type represents exact signed decimal values. Decimal repres scalar Decimal input Decimal_filter { - eq: [Decimal] - ge: [Decimal] - gt: [Decimal] - le: [Decimal] - lt: [Decimal] + eq: Decimal + ge: Decimal + gt: Decimal + le: Decimal + lt: Decimal ne: [Decimal] } input Float_filter { - eq: [Float] - ge: [Float] - gt: [Float] - le: [Float] - lt: [Float] + eq: Float + ge: Float + gt: Float + le: Float + lt: Float ne: [Float] } input ID_filter { - eq: [ID] - ge: [ID] - gt: [ID] - le: [ID] - lt: [ID] + eq: ID + ge: ID + gt: ID + le: ID + lt: ID ne: [ID] } @@ -79,11 +79,11 @@ The `Int16` scalar type represents 16-bit non-fractional signed whole numeric va scalar Int16 input Int16_filter { - eq: [Int16] - ge: [Int16] - gt: [Int16] - le: [Int16] - lt: [Int16] + eq: Int16 + ge: Int16 + gt: Int16 + le: Int16 + lt: Int16 ne: [Int16] } @@ -93,20 +93,20 @@ The `Int64` scalar type represents 64-bit non-fractional signed whole numeric va scalar Int64 input Int64_filter { - eq: [Int64] - ge: [Int64] - gt: [Int64] - le: [Int64] - lt: [Int64] + eq: Int64 + ge: Int64 + gt: Int64 + le: Int64 + lt: Int64 ne: [Int64] } input Int_filter { - eq: [Int] - ge: [Int] - gt: [Int] - le: [Int] - lt: [Int] + eq: Int + ge: Int + gt: Int + le: Int + lt: Int ne: [Int] } @@ -125,14 +125,14 @@ enum SortDirection { input String_filter { contains: [String] - endswith: [String] - eq: [String] - ge: [String] - gt: [String] - le: [String] - lt: [String] + endswith: String + eq: String + ge: String + gt: String + le: String + lt: String ne: [String] - startswith: [String] + startswith: String } """ @@ -141,11 +141,11 @@ The `Time` scalar type represents time values as strings in the ISO 8601 format scalar Time input Time_filter { - eq: [Time] - ge: [Time] - gt: [Time] - le: [Time] - lt: [Time] + eq: Time + ge: Time + gt: Time + le: Time + lt: Time ne: [Time] } @@ -155,11 +155,11 @@ The `Timestamp` scalar type represents timestamp values as strings in the ISO 86 scalar Timestamp input Timestamp_filter { - eq: [Timestamp] - ge: [Timestamp] - gt: [Timestamp] - le: [Timestamp] - lt: [Timestamp] + eq: Timestamp + ge: Timestamp + gt: Timestamp + le: Timestamp + lt: Timestamp ne: [Timestamp] } @@ -309,10 +309,10 @@ The `UInt8` scalar type represents 8-bit non-fractional unsigned whole numeric v scalar UInt8 input UInt8_filter { - eq: [UInt8] - ge: [UInt8] - gt: [UInt8] - le: [UInt8] - lt: [UInt8] + eq: UInt8 + ge: UInt8 + gt: UInt8 + le: UInt8 + lt: UInt8 ne: [UInt8] } diff --git a/test/tests/queries/variables.test.js b/test/tests/queries/variables.test.js index e016c104..91bfbf94 100644 --- a/test/tests/queries/variables.test.js +++ b/test/tests/queries/variables.test.js @@ -111,7 +111,7 @@ describe('graphql - variables', () => { test('query variable of type scalar value passed as a field of an argument', async () => { const query = gql` - query ($filter: [Int]) { + query ($filter: Int) { AdminServiceBasic { Books(filter: { ID: { ge: $filter } }) { ID @@ -254,7 +254,7 @@ describe('graphql - variables', () => { test('query variable of type scalar value passed as a field of an argument', async () => { const query = gql` - query ($filter: [Int]) { + query ($filter: Int) { AdminService { Books(filter: { ID: { ge: $filter } }) { nodes {