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:10 UTC
(grails-core) 02/03: Fix control flow and migrate deprecated typeResolver in AbstractObjectTypeBuilder
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch refactor/graphql-types
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 9e3929a9acc6dd144ae4229dc63952f0832eab6a
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sun Aug 23 09:27:56 2026 -0500
Fix control flow and migrate deprecated typeResolver in AbstractObjectTypeBuilder
- build(entity) declared a blank GraphQLOutputType reassigned across
if/else branches then passed to objectTypeCache.put(...) - Groovy's
static type checker narrowed its inferred type to an intersection of
GraphQLInterfaceType/GraphQLObjectType's common interfaces rather than
the declared GraphQLOutputType, so put() no longer type-checked.
Rewrote to return the cached value directly and assign objectType via
a ternary instead of branch reassignment, the same treatment given to
DefaultGormDataFetcher.resolveDatastore() earlier in this cleanup.
- Fixed an unrelated latent bug found while touching build(): fields'
ArrayList capacity was computed from `properties.size()` one line
before `properties` was declared, which under CompileStatic actually
resolved to the GroovyObject-inherited `getProperties()` bean-property
map (an unrelated count), not the domain property list. Moved the
properties declaration first.
- buildInterfaceType(entity, name, description, fields) never used its
entity parameter; dropped it (no overrides exist, undocumented).
- GraphQLInterfaceType.Builder#typeResolver(TypeResolver) is deprecated
since 2018-12-03 in favor of registering the resolver on
GraphQLCodeRegistry.Builder by interface name, same migration already
applied to CustomOperation/Schema's dataFetcher registration earlier
in this cleanup.
Added AbstractObjectTypeBuilderSpec, the first coverage of build()'s
interface-type branch (a root entity with child entities) - the other
object type builder specs construct their builder with a null
GraphQLTypeManager and can't reach build() at all. Backs it with a
minimal Animal/Dog GORM inheritance fixture and verifies the interface
type, the child's implements-interface relationship, and that the
migrated codeRegistry type resolver is actually registered.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../types/output/AbstractObjectTypeBuilder.groovy | 61 ++++++++----------
.../gorm/graphql/domain/inheritance/Animal.groovy | 29 +++++++++
.../gorm/graphql/domain/inheritance/Dog.groovy | 29 +++++++++
.../domain/inheritance/InheritancePackage.groovy | 23 +++++++
.../output/AbstractObjectTypeBuilderSpec.groovy | 72 ++++++++++++++++++++++
5 files changed, 180 insertions(+), 34 deletions(-)
diff --git a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilder.groovy b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilder.groovy
index 1d8a4dd104..e527619f84 100644
--- a/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilder.groovy
+++ b/grails-data-graphql/core/src/main/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilder.groovy
@@ -101,44 +101,36 @@ abstract class AbstractObjectTypeBuilder implements ObjectTypeBuilder {
@Override
GraphQLOutputType build(PersistentEntity entity) {
-
- GraphQLOutputType objectType
-
if (objectTypeCache.containsKey(entity)) {
- objectTypeCache.get(entity)
+ return objectTypeCache.get(entity)
}
- else {
- final String description = GraphQLEntityHelper.getDescription(entity)
- final String name = typeManager.namingConvention.getType(entity, type)
-
- List<GraphQLFieldDefinition> fields = new ArrayList<>(properties.size() + 1)
-
- List<GraphQLDomainProperty> properties = builder.getProperties(entity)
- for (GraphQLDomainProperty prop: properties) {
- if (prop.output) {
- GraphQLFieldDefinition.Builder field = buildField(prop, name)
- addFieldArgs(field, prop, entity.mappingContext)
- fields.add(field.build())
- }
- }
- if (errorsResponseHandler != null) {
- GraphQLFieldDefinition fieldDefinition = errorsResponseHandler.getFieldDefinition(typeManager, name)
- fields.add(fieldDefinition)
- }
-
- boolean hasChildEntities = entity.root && !entity.mappingContext.getDirectChildEntities(entity).empty
+ final String description = GraphQLEntityHelper.getDescription(entity)
+ final String name = typeManager.namingConvention.getType(entity, type)
- if (hasChildEntities && !type.embedded) {
- objectType = buildInterfaceType(entity, name, description, fields)
- }
- else {
- objectType = buildObjectType(entity, name, description, fields)
+ List<GraphQLDomainProperty> properties = builder.getProperties(entity)
+ List<GraphQLFieldDefinition> fields = new ArrayList<>(properties.size() + 1)
+ for (GraphQLDomainProperty prop: properties) {
+ if (prop.output) {
+ GraphQLFieldDefinition.Builder field = buildField(prop, name)
+ addFieldArgs(field, prop, entity.mappingContext)
+ fields.add(field.build())
}
+ }
- objectTypeCache.put(entity, objectType)
- objectType
+ if (errorsResponseHandler != null) {
+ GraphQLFieldDefinition fieldDefinition = errorsResponseHandler.getFieldDefinition(typeManager, name)
+ fields.add(fieldDefinition)
}
+
+ boolean hasChildEntities = entity.root && !entity.mappingContext.getDirectChildEntities(entity).empty
+
+ GraphQLOutputType objectType = hasChildEntities && !type.embedded ?
+ buildInterfaceType(name, description, fields) :
+ buildObjectType(entity, name, description, fields)
+
+ objectTypeCache.put(entity, objectType)
+ objectType
}
GraphQLObjectType buildObjectType(final PersistentEntity entity, final String name, final String description, final List<GraphQLFieldDefinition> fields) {
@@ -155,15 +147,16 @@ abstract class AbstractObjectTypeBuilder implements ObjectTypeBuilder {
obj.build()
}
- GraphQLInterfaceType buildInterfaceType(final PersistentEntity entity, final String name, final String description, final List<GraphQLFieldDefinition> fields) {
+ GraphQLInterfaceType buildInterfaceType(final String name, final String description, final List<GraphQLFieldDefinition> fields) {
GraphQLInterfaceType.Builder obj = newInterface()
.name(name)
.description(description)
.fields(fields)
- .typeResolver(buildEntityTypeResolver())
- obj.build()
+ GraphQLInterfaceType interfaceType = obj.build()
+ codeRegistry.typeResolver(name, buildEntityTypeResolver())
+ interfaceType
}
protected TypeResolver buildEntityTypeResolver() {
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Animal.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Animal.groovy
new file mode 100644
index 0000000000..7249cd9578
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Animal.groovy
@@ -0,0 +1,29 @@
+/*
+ * 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.domain.inheritance
+
+import grails.gorm.annotation.Entity
+
+@Entity
+class Animal {
+ String name
+
+ static graphql = true
+}
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Dog.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Dog.groovy
new file mode 100644
index 0000000000..226f3dfb38
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/Dog.groovy
@@ -0,0 +1,29 @@
+/*
+ * 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.domain.inheritance
+
+import grails.gorm.annotation.Entity
+
+@Entity
+class Dog extends Animal {
+ String breed
+
+ static graphql = true
+}
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/InheritancePackage.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/InheritancePackage.groovy
new file mode 100644
index 0000000000..5f178bede9
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/domain/inheritance/InheritancePackage.groovy
@@ -0,0 +1,23 @@
+/*
+ * 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.domain.inheritance
+
+class InheritancePackage {
+}
diff --git a/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilderSpec.groovy b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilderSpec.groovy
new file mode 100644
index 0000000000..543549b87c
--- /dev/null
+++ b/grails-data-graphql/core/src/test/groovy/org/grails/gorm/graphql/types/output/AbstractObjectTypeBuilderSpec.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.types.output
+
+import graphql.schema.GraphQLInterfaceType
+import graphql.schema.GraphQLObjectType
+import graphql.schema.GraphQLSchema
+import org.grails.datastore.mapping.config.Settings
+import org.grails.datastore.mapping.core.DatastoreUtils
+import org.grails.gorm.graphql.Schema
+import org.grails.gorm.graphql.domain.inheritance.InheritancePackage
+import org.grails.orm.hibernate.HibernateDatastore
+import spock.lang.AutoCleanup
+import spock.lang.Shared
+import spock.lang.Specification
+
+/**
+ * Exercises AbstractObjectTypeBuilder#build(PersistentEntity) through a real
+ * Schema, covering the interface-type branch (a root entity with child
+ * entities) which none of the other object type builder specs reach, since
+ * they construct their builders with a null GraphQLTypeManager.
+ */
+class AbstractObjectTypeBuilderSpec extends Specification {
+
+ @Shared @AutoCleanup HibernateDatastore hibernateDatastore
+ @Shared GraphQLSchema schema
+
+ void setupSpec() {
+ hibernateDatastore = new HibernateDatastore(
+ DatastoreUtils.createPropertyResolver(Collections.singletonMap(Settings.SETTING_DB_CREATE, 'create-drop')),
+ InheritancePackage.getPackage())
+ schema = new Schema(hibernateDatastore.mappingContext).generate()
+ }
+
+ void "test a root entity with child entities builds an interface type"() {
+ expect:
+ schema.getType('Animal') instanceof GraphQLInterfaceType
+ }
+
+ void "test a child entity builds an object type that implements the root interface"() {
+ given:
+ GraphQLInterfaceType animalType = (GraphQLInterfaceType) schema.getType('Animal')
+ GraphQLObjectType dogType = (GraphQLObjectType) schema.getType('Dog')
+
+ expect:
+ dogType.interfaces.contains(animalType)
+ }
+
+ void "test a type resolver is registered for the interface type"() {
+ given:
+ GraphQLInterfaceType animalType = (GraphQLInterfaceType) schema.getType('Animal')
+
+ expect:
+ schema.codeRegistry.getTypeResolver(animalType) != null
+ }
+}