You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/04/08 04:04:47 UTC
(grails-core) 01/01: GRAILS-10683 - improve rendering empty collections
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch expandedTests/GRAILS-10683b
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit e633d4e7404b9c88a72777a6ad6806b36b7dec4d
Author: Jeff Scott Brown <jb...@gopivotal.com>
AuthorDate: Tue Dec 3 11:18:39 2013 -0600
GRAILS-10683 - improve rendering empty collections
---
.../plugins/web/rest/api/ControllersRestApi.groovy | 34 ++++++-
.../web/rest/render/DefaultRendererRegistry.groovy | 2 +
.../web/rest/transform/ResourceTransform.groovy | 1 +
.../test/mixin/web/ControllerUnitTestMixin.groovy | 6 +-
.../RespondMethodWithEmptyCollectionSpec.groovy | 106 +++++++++++++++++++++
5 files changed, 141 insertions(+), 8 deletions(-)
diff --git a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/api/ControllersRestApi.groovy b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/api/ControllersRestApi.groovy
index d505edabd5..fe9515f581 100644
--- a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/api/ControllersRestApi.groovy
+++ b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/api/ControllersRestApi.groovy
@@ -80,6 +80,25 @@ class ControllersRestApi {
* @return
*/
Object respond(Object controller, Object value, Map args = [:]) {
+ respond controller, null, value, args
+ }
+
+ /**
+ * The respond method will attempt to delivery an appropriate response for the
+ * requested response format and value.
+ *
+ * If the value is null then a 404 will be returned. Otherwise the {@link RendererRegistry}
+ * will be consulted for an appropriate response renderer for the requested response format.
+ *
+ * @param controller The controller
+ * @param componentType The type of component being rendered. If null the type will be the
+ * type of value unless value is a Collection in which case the type will be the type of
+ * the first element in the collection. This value is used to locate an appropriate Renderer.
+ * @param value The value
+ * @param args The arguments
+ * @return
+ */
+ Object respond(Object controller, Class componentType, Object value, Map args = [:]) {
Integer statusCode
if (args.status) {
final Object statusValue = args.status
@@ -94,7 +113,7 @@ class ControllersRestApi {
}
final webRequest = getWebRequest(controller)
- List<String> formats = calculateFormats(controller, webRequest.actionName, value, args)
+ List<String> formats = calculateFormats(controller, webRequest.actionName, componentType ?: value, args)
final response = webRequest.getCurrentResponse()
MimeType mimeType = getResponseFormat(response)
def registry = rendererRegistry
@@ -134,12 +153,12 @@ class ControllersRestApi {
final valueType = value.getClass()
if (registry.isContainerType(valueType)) {
- renderer = registry.findContainerRenderer(mimeType,valueType, value)
+ renderer = registry.findContainerRenderer(mimeType,valueType, componentType ?: value)
if (renderer == null) {
- renderer = registry.findRenderer(mimeType, value)
+ renderer = registry.findRenderer(mimeType, componentType ?: value)
}
} else {
- renderer = registry.findRenderer(mimeType, value)
+ renderer = registry.findRenderer(mimeType, componentType ?: value)
}
if (renderer) {
@@ -181,7 +200,12 @@ class ControllersRestApi {
}
protected List<String> getDefaultResponseFormats(value) {
- Resource resAnn = value != null ? value.getClass().getAnnotation(Resource) : null
+ Resource resAnn
+ if(value instanceof Class) {
+ resAnn = value.getAnnotation(Resource)
+ } else {
+ resAnn = value != null ? value.getClass().getAnnotation(Resource) : null
+ }
if (resAnn) {
return resAnn.formats().toList()
}
diff --git a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/render/DefaultRendererRegistry.groovy b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/render/DefaultRendererRegistry.groovy
index db1e2103a7..bf86ac9ab4 100644
--- a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/render/DefaultRendererRegistry.groovy
+++ b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/render/DefaultRendererRegistry.groovy
@@ -184,6 +184,8 @@ class DefaultRendererRegistry extends ClassAndMimeTypeRegistry<Renderer, Rendere
Class targetClass = containerClass
if (containerClass.isArray()) {
targetClass = containerClass.getComponentType()
+ } else if(object instanceof Class) {
+ targetClass = object
} else if (object instanceof Iterable) {
if (object) {
final iterator = object.iterator()
diff --git a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/transform/ResourceTransform.groovy b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/transform/ResourceTransform.groovy
index c595e7986c..09f3c63fd4 100644
--- a/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/transform/ResourceTransform.groovy
+++ b/grails-plugin-rest/src/main/groovy/org/grails/plugins/web/rest/transform/ResourceTransform.groovy
@@ -505,6 +505,7 @@ class ResourceTransform implements ASTTransformation{
def listCall = applyDefaultMethodTarget(new MethodCallExpression(domainExpr, new ConstantExpression("list"), listArgs), domainClass)
def countCall = applyDefaultMethodTarget(new MethodCallExpression(domainExpr, new ConstantExpression("count"), MethodCallExpression.NO_ARGUMENTS), domainClass)
def respondArgs = new ArgumentListExpression()
+ respondArgs.addExpression(domainExpr)
respondArgs.addExpression(listCall)
final args = new MapExpression()
final model = new MapExpression()
diff --git a/grails-plugin-testing/src/main/groovy/grails/test/mixin/web/ControllerUnitTestMixin.groovy b/grails-plugin-testing/src/main/groovy/grails/test/mixin/web/ControllerUnitTestMixin.groovy
index 71f2cbd726..70d711b0c5 100644
--- a/grails-plugin-testing/src/main/groovy/grails/test/mixin/web/ControllerUnitTestMixin.groovy
+++ b/grails-plugin-testing/src/main/groovy/grails/test/mixin/web/ControllerUnitTestMixin.groovy
@@ -182,9 +182,9 @@ class ControllerUnitTestMixin extends GrailsUnitTestMixin {
defineBeans(new ConvertersGrailsPlugin().doWithSpring)
defineBeans {
instanceControllersApi(ControllersApi)
- final rendererRegistry = new DefaultRendererRegistry()
- rendererRegistry.modelSuffix = config.flatten().get('grails.scaffolding.templates.domainSuffix') ?: ''
- rendererRegistry.initialize()
+ rendererRegistry(DefaultRendererRegistry) {
+ modelSuffix = config.flatten().get('grails.scaffolding.templates.domainSuffix') ?: ''
+ }
instanceControllersRestApi(ControllersRestApi, rendererRegistry, ref("instanceControllersApi"), new ControllersMimeTypesApi())
instanceControllerTagLibraryApi(ControllerTagLibraryApi)
diff --git a/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodWithEmptyCollectionSpec.groovy b/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodWithEmptyCollectionSpec.groovy
new file mode 100644
index 0000000000..dc48b4dc62
--- /dev/null
+++ b/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodWithEmptyCollectionSpec.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * 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.
+ */
+
+package grails.rest.web
+
+import grails.artefact.Artefact
+import grails.persistence.Entity
+import grails.rest.Resource
+import grails.rest.render.json.JsonCollectionRenderer
+import grails.test.mixin.Mock
+import grails.test.mixin.TestFor
+
+import org.codehaus.groovy.grails.plugins.web.mimes.MimeTypesFactoryBean
+
+import spock.lang.Ignore
+import spock.lang.Issue
+import spock.lang.Specification
+
+@TestFor(GadgetController)
+@Mock(Gadget)
+class RespondMethodWithEmptyCollectionSpec extends Specification{
+
+ void setup() {
+ def ga = grailsApplication
+ ga.config.grails.mime.types =
+ [ html: ['text/html','application/xhtml+xml'],
+ xml: ['text/xml', 'application/xml'],
+ text: 'text/plain',
+ js: 'text/javascript',
+ rss: 'application/rss+xml',
+ atom: 'application/atom+xml',
+ css: 'text/css',
+ csv: 'text/csv',
+ all: '*/*',
+ json: ['application/json','text/json'],
+ form: 'application/x-www-form-urlencoded',
+ multipartForm: 'multipart/form-data'
+ ]
+
+ }
+
+ def setupSpec() {
+ defineBeans {
+ mimeTypes(MimeTypesFactoryBean) {
+ grailsApplication = ref('grailsApplication')
+ }
+ gadgetCollectionRenderer(JsonCollectionRenderer, Gadget, 'application/json') {
+ excludes=['class']
+ }
+ }
+ }
+
+ @Issue('GRAILS-10683')
+ void "Test responding to a REST call with an empty collection"() {
+
+ when:
+ response.format = 'json'
+ controller.index()
+
+ then:
+ response.status == 200
+ response.contentAsString == '[]'
+ }
+
+ @Issue('GRAILS-10683')
+ @Ignore
+ void "Test responding to an HTML call with an empty collection"() {
+ when:
+ response.format = 'html'
+ controller.index()
+
+ then:
+ response.status == 200
+ }
+}
+
+@Artefact("Controller")
+class GadgetController {
+
+ def index() {
+ respond Gadget, []
+ }
+}
+@Entity
+@Resource(formats=['json', 'html'])
+class Gadget {
+ String title
+
+ static constraints = {
+ title blank:false
+ }
+}
+