You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/08/23 16:04:36 UTC
(grails-core) 06/08: Replace deprecated GraphQLArgument/GraphQLInputObjectField defaultValue calls
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch refactor/graphql-entity
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 83cc487a5cc11da9637c7af447a0c83dbbc8e89a
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Fri Aug 21 23:21:12 2026 -0500
Replace deprecated GraphQLArgument/GraphQLInputObjectField defaultValue calls
graphql-java 25 deprecates Builder#defaultValue(Object) in favor of
defaultValueLiteral(Value) or defaultValueProgrammatic(Object).
CustomArgument and ComplexTyped supply a plain, uncoerced Java value
from the DSL, so defaultValueProgrammatic is the correct replacement.
Switching unconditionally surfaced a real bug: the old deprecated call
always marked the default value as "set" (even when the DSL user never
configured one), which the old INTERNAL_VALUE state silently exempted
from schema validation. defaultValueProgrammatic's EXTERNAL_VALUE state
is validated, and a null default on a non-null argument/field type
failed schema build. Both call sites now only apply a default value
when one was actually configured via the DSL, leaving it unset
otherwise - matching the DSL's actual intent and passing validation.
Also documents (without changing) the unqualified withDelegate() calls
in Arguable and ComplexTyped: explicitly qualifying the inherited
ExecutesClosures static trait method there does not resolve under
@CompileStatic, so the plain call must stay despite IDE inspections
suggesting otherwise.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../graphql/entity/arguments/CustomArgument.groovy | 9 ++-
.../graphql/entity/dsl/helpers/Arguable.groovy | 4 +-
.../graphql/entity/dsl/helpers/ComplexTyped.groovy | 16 +++-
.../entity/arguments/SimpleArgumentSpec.groovy | 89 ++++++++++++++++++++++
.../entity/dsl/helpers/ComplexTypedSpec.groovy | 69 +++++++++++++++++
5 files changed, 180 insertions(+), 7 deletions(-)
diff --git a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/arguments/CustomArgument.groovy b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/arguments/CustomArgument.groovy
index 8d3b52eaea..10ae751d12 100644
--- a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/arguments/CustomArgument.groovy
+++ b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/arguments/CustomArgument.groovy
@@ -49,11 +49,16 @@ abstract class CustomArgument<T> implements Named<T>, Describable<T>, Nullable<T
GraphQLArgument.Builder getArgument(GraphQLTypeManager typeManager, MappingContext mappingContext) {
GraphQLInputType type = getType(typeManager, mappingContext)
- newArgument()
+ GraphQLArgument.Builder builder = newArgument()
.name(name)
.description(description)
- .defaultValue(defaultValue)
.type(type)
+
+ if (defaultValue != null) {
+ builder.defaultValueProgrammatic(defaultValue)
+ }
+
+ builder
}
void validate() {
diff --git a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/Arguable.groovy b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/Arguable.groovy
index 2a6409e3e8..826709900f 100644
--- a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/Arguable.groovy
+++ b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/Arguable.groovy
@@ -41,7 +41,9 @@ trait Arguable<T> extends ExecutesClosures {
private void handleArgumentClosure(CustomArgument argument, @DelegatesTo(strategy = Closure.DELEGATE_ONLY)Closure closure) {
// GROOVY-12106: STC could not resolve this inherited ExecutesClosures static from a sub-trait
- // body; fixed in Groovy 5.0.7, so the plain inherited-static call compiles again.
+ // body; fixed in Groovy 5.0.7, so the plain inherited-static call compiles again. Qualifying
+ // this call (e.g. ExecutesClosures.withDelegate(...)) does NOT resolve under STC either - only
+ // the unqualified form works, so leave it as-is despite IDE inspections suggesting otherwise.
withDelegate(closure, (Object) argument)
argument.validate()
arguments.add(argument)
diff --git a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTyped.groovy b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTyped.groovy
index 128b18eacf..7f537e3cb0 100644
--- a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTyped.groovy
+++ b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTyped.groovy
@@ -19,6 +19,7 @@
package org.grails.gorm.graphql.entity.dsl.helpers
+import graphql.schema.GraphQLInputObjectField
import graphql.schema.GraphQLInputObjectType
import graphql.schema.GraphQLInputType
import graphql.schema.GraphQLList
@@ -107,11 +108,16 @@ trait ComplexTyped<T> extends ExecutesClosures {
for (Field field: fields) {
if (field.input) {
- builder.field(newInputObjectField()
+ GraphQLInputObjectField.Builder fieldBuilder = newInputObjectField()
.name(field.name)
.description(field.description)
- .defaultValue(field.defaultValue)
- .type(field.getInputType(typeManager, mappingContext)))
+ .type(field.getInputType(typeManager, mappingContext))
+
+ if (field.defaultValue != null) {
+ fieldBuilder.defaultValueProgrammatic(field.defaultValue)
+ }
+
+ builder.field(fieldBuilder)
}
}
GraphQLInputType type = builder.build()
@@ -132,7 +138,9 @@ trait ComplexTyped<T> extends ExecutesClosures {
private void handleField(@DelegatesTo(strategy = Closure.DELEGATE_ONLY)Closure closure, Field field) {
field.nullable(defaultNull)
// GROOVY-12106: STC could not resolve this inherited ExecutesClosures static from a sub-trait
- // body; fixed in Groovy 5.0.7, so the plain inherited-static call compiles again.
+ // body; fixed in Groovy 5.0.7, so the plain inherited-static call compiles again. Qualifying
+ // this call (e.g. ExecutesClosures.withDelegate(...)) does NOT resolve under STC either - only
+ // the unqualified form works, so leave it as-is despite IDE inspections suggesting otherwise.
withDelegate(closure, (Object) field)
handleField(field)
}
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/arguments/SimpleArgumentSpec.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/arguments/SimpleArgumentSpec.groovy
new file mode 100644
index 0000000000..21f820b586
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/arguments/SimpleArgumentSpec.groovy
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * https://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.
+ */
+
+package org.grails.gorm.graphql.entity.arguments
+
+import graphql.Scalars
+import graphql.schema.GraphQLArgument
+import org.grails.datastore.mapping.model.MappingContext
+import org.grails.gorm.graphql.types.GraphQLTypeManager
+import spock.lang.Specification
+
+class SimpleArgumentSpec extends Specification {
+
+ GraphQLTypeManager typeManager = Stub(GraphQLTypeManager) {
+ hasType(String) >> true
+ getType(String, _ as boolean) >> Scalars.GraphQLString
+ }
+ MappingContext mappingContext = Stub(MappingContext)
+
+ void "test getArgument sets an externally provided default value"() {
+ given:
+ SimpleArgument argument = new SimpleArgument()
+ .name('foo')
+ .description('a foo argument')
+ .returns(String)
+ .defaultValue('bar')
+
+ when:
+ GraphQLArgument built = argument.getArgument(typeManager, mappingContext).build()
+
+ then:
+ built.name == 'foo'
+ built.description == 'a foo argument'
+ built.argumentDefaultValue.set
+ built.argumentDefaultValue.external
+ built.argumentDefaultValue.value == 'bar'
+ }
+
+ void "test getArgument leaves the default value not set when none is configured"() {
+ given:
+ SimpleArgument argument = new SimpleArgument()
+ .name('foo')
+ .returns(String)
+
+ when:
+ GraphQLArgument built = argument.getArgument(typeManager, mappingContext).build()
+
+ then:
+ !built.argumentDefaultValue.set
+ }
+
+ void "test validate requires a return type"() {
+ given:
+ SimpleArgument argument = new SimpleArgument().name('foo')
+
+ when:
+ argument.validate()
+
+ then:
+ thrown(IllegalArgumentException)
+ }
+
+ void "test validate passes when a name and return type are set"() {
+ given:
+ SimpleArgument argument = new SimpleArgument().name('foo').returns(String)
+
+ when:
+ argument.validate()
+
+ then:
+ noExceptionThrown()
+ }
+}
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTypedSpec.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTypedSpec.groovy
new file mode 100644
index 0000000000..c385b6e1c7
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/entity/dsl/helpers/ComplexTypedSpec.groovy
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * https://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.
+ */
+
+package org.grails.gorm.graphql.entity.dsl.helpers
+
+import graphql.Scalars
+import graphql.schema.GraphQLInputObjectField
+import graphql.schema.GraphQLInputObjectType
+import org.grails.datastore.mapping.model.MappingContext
+import org.grails.gorm.graphql.entity.fields.SimpleField
+import org.grails.gorm.graphql.types.GraphQLTypeManager
+import spock.lang.Specification
+
+class ComplexTypedSpec extends Specification {
+
+ GraphQLTypeManager typeManager = Stub(GraphQLTypeManager) {
+ hasType(String) >> true
+ getType(String, _ as boolean) >> Scalars.GraphQLString
+ }
+ MappingContext mappingContext = Stub(MappingContext)
+
+ ComplexTyped buildComplexTyped() {
+ (ComplexTyped) new Object().withTraits(ComplexTyped)
+ }
+
+ void "test buildCustomInputType sets an externally provided default value on its fields"() {
+ given:
+ ComplexTyped complexTyped = buildComplexTyped()
+ complexTyped.fields.add(new SimpleField().name('foo').returns(String).defaultValue('bar'))
+
+ when:
+ GraphQLInputObjectType type = (GraphQLInputObjectType) complexTyped.buildCustomInputType('Foo', typeManager, mappingContext, true)
+ GraphQLInputObjectField field = type.getFieldDefinition('foo')
+
+ then:
+ field.inputFieldDefaultValue.set
+ field.inputFieldDefaultValue.external
+ field.inputFieldDefaultValue.value == 'bar'
+ }
+
+ void "test buildCustomInputType leaves the default value not set when none is configured"() {
+ given:
+ ComplexTyped complexTyped = buildComplexTyped()
+ complexTyped.fields.add(new SimpleField().name('foo').returns(String))
+
+ when:
+ GraphQLInputObjectType type = (GraphQLInputObjectType) complexTyped.buildCustomInputType('Foo', typeManager, mappingContext, true)
+ GraphQLInputObjectField field = type.getFieldDefinition('foo')
+
+ then:
+ !field.inputFieldDefaultValue.set
+ }
+}