I am using grails 2.5.5 and the problem is when I'm using a command Object, I want to change the format of the data I get with the #BindUsing annotation.
#Validatable
class FooCommand extends BarCommand{
#BindUsing({obj, source -> return source['foo'].collect{it['id']}})
List<Long> foo
static constraints = {foo(nullable:true)}
}
the BindUsing closure never gets executed. What is the problem and how to solve?
JSONData = {"foo":[
{'id':5},
{'id':4}
]
}
FooCommand fooCommand = FooCommand(JSONData)
fooCommand.validate()
EDIT:
To be clear: the problem is that the data change from format,from list of maps, to just a list of longs. Is there a way to do this with the BindUsing or will i just have to do it in the controller each time?
Related
I'm looking for a simple way to check some types in my params. If they exist they should have a certain type, if not they should have a default value. If they exist and their type is wrong, an exception should be thrown.
It would be nice to have the values in a handy container afterwards. Therefore I've tried passing params to the container's constructor and bindData on a container object. Neither is successful:
class ContainerClass {
Integer foo = 42;
}
class TestController {
def index(){
//doesn't throw, **even when params.foo = "2asdf3"**
ContainerClass meh = new ContainerClass();
bindData(meh, params);
println meh.foo //prints 42 when params.foo = "2asdf3"
// throws GroovyCastException: Cannot cast object '23' with class
// 'java.lang.String' to to class 'java.lang.Integer',
// **even when params.foo = "23"**
ContainerClass meh2 = new ContainerClass(params);
render "meh"
}
}
I'm using grails 3 if this is of importance.
You might use Command Objects to bind parameters to certain Data Type.
Reference : http://docs.grails.org/latest/guide/single.html?utm_content=bufferf77f5&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer#commandObjects
If you want to throw exceptions if certain data type does not match,that needs to be checked explicitly.
Given the Grails Domain Classes "Apple.groovy" and "Orange.groovy", not strictly related, and the following Grails Service invoked from the Controllers:
package test
import grails.transaction.Transactional
import javax.servlet.http.HttpServletRequest
#Transactional
class UploadService {
def uploadApple(HttpServletRequest request, Apple o) {
def file = request.getFile('payload')
o.payload = file.getBytes()
o.filename = file.originalFilename
o.contentType = file.contentType
o.save(flush:true)
}
def uploadOrange(HttpServletRequest request, Orange o) {
def file = request.getFile('payload')
o.payload = file.getBytes()
o.filename = file.originalFilename
o.contentType = file.contentType
o.save(flush:true)
}
}
How would one go about unifying this code under a common method? I was hoping Groovy's optional types would handle this for me, but I can't seem to be able invoke .save() successfully if I remove the types from the method signature.
Thanks in advance
Avoid passing request to service and pass File to service. Did you try it this way?
def upload(file, obj) {
obj.payload = file.getBytes()
obj.filename = file.originalFilename
obj.contentType = file.contentType
obj.save(flush:true)
}
Unlike Java, Groovy does duck typing. Typing is concerned with assigning a type to any object. Duck typing is concerned with establishing the suitability of an object for some purpose.
Now you have to be careful because this will not work for every object. The reason that it works for Apple and Orange is because both have exactly the same set of attributes and attribute types.
One would wonder, why you would have two different domain that behave exactly the same, but that's obviously is a different discussion.
In Grails 3, domain classes implement the GormEntity trait, which is where the save() method comes from. But that doesn't solve the issue with the payload, filename, and contentType properties.
What you can do is create an interface which declares the methods and properties common to both domain classes and also implements org.grails.datastore.gorm.GormEntity. Then, have the domain classes implement the interface:
interface Uploadable extends GormEntity {
byte [] payload
String filename
String contentType
}
class Apple implements Uploadable {
byte [] payload
String filename
String contentType
}
Then, you can use the interface in your service.
#Transactional
class UploadService {
def upload(HttpServletRequest request, Uploadable o) {
def file = request.getFile('payload')
o.payload = file.getBytes()
o.filename = file.originalFilename
o.contentType = file.contentType
o.save(flush:true)
}
}
Note: Since Grails injects the GormEntity trait into the domain classes automatically, I don't know what happens if you use it explicitly as shown.
Suppose I have,
public class Ctrl{
#Named #Produces public My getMy(){ return new My(); }
I can use EL expressions with my in it. So that's OK.
But in the My type itself, I cannot inject, for example,
public class My
#Inject #Random private int randomNumber;
The randomNumber field in my above is null.
So my question is: Can you indeed not inject in a produced object, or am I doing something wrong?
Since you manually instantiate your My class, it will not have dependencies inject. That is correct.
There are ways around this though. If you're using DeltaSpike, you can use BeanProvider.injectFields to populate the injection points of your bean before returning it.
If you're not using DeltaSpike, have a peak at the code to see what you can do to port the change ( https://github.com/apache/deltaspike/blob/master/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/provider/BeanProvider.java#L395 ) :
BeanManager beanManager = ....; //however you get your bean manager.
CreationalContext creationalContext = beanManager.createCreationalContext(null);
AnnotatedType annotatedType = beanManager.createAnnotatedType(instance.getClass());
InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType);
injectionTarget.inject(instance, creationalContext);
Similar to my last question (Grails databinding: creating instances of an abstract class), I want to use data binding with a class that contains a collection of abstract classes with a hasMany relationship, but in this case, instead of using a List, I'm using a Map.
I created a smalll project with a failing integration test to show the problem that can be found in Github, run it with:
grails test-app -integration -echoOut DataBinding
Anyway, I'll explain the problem by describing the classes and the test here:
class LocalizableContent {
Map contentByLocale = [:].withDefault { locale -> new Text() }
static hasMany = [ contentByLocale : Content ]
}
abstract class Content {
static belongsTo = [ localizableContent : LocalizableContent ]
static constraints = {
localizableContent nullable:true
}
}
class Text extends Content {
String text
}
As you can see, I'm already using the withDefault trick, but apparently it's not being called by Grails / Spring (I even tried to throw an exception in the default closure to verify that the code is not executed).
For the sake of the test, I also created a LocalizableContentController which is empty. With all that, the following integration test then fails:
void testMapDatabinding() {
def rawParams = [ 'contentByLocale[en].text': 'Content' ]
def controller = new LocalizableContentController()
controller.request.addParameters(rawParams)
controller.request.setAttribute(GrailsApplicationAttributes.CONTROLLER, controller)
def localizableContent = new LocalizableContent(controller.params)
assert localizableContent?.contentByLocale['en']?.text == 'Content'
}
It says that localizableContent.contentByLocale is a map which looks like ['en': null], so apparently the data binding is understanding the map syntax and trying to create an entry for the 'en' key. But is not trying first to get the entry for that key, since the withDefault is not being called.
The following one tests that the withDefault works fine, and it passes:
void testMapByDefaultWithNoDatabinding() {
assert new LocalizableContent().contentByLocale['en']?.getClass() == Text
}
What am I missing here?
withDefault is nothing but a pattern to provide a valid value if you face an unknown key. For example, consider the below use case:
def map = [:].withDefault{k->
println k //Should print 'a'
10
}
map.test = 32
assert map.test == 32
assert map.a == 10
It takes the unknown key as the parameter, you cannot pass in any value to it, which is kind of logical, because it provides a default value instead of a value being provided.
In your case, the data binding would work if set the value to Text like:
Map contentByLocale = [:].withDefault { locale ->
//locale is the key. 'en' in this case
new Text(locale: locale, text: 'Content')
}
provided you have your Text class defined as
class Text extends Content{
String locale
String text
}
The following is not working for me when using abstract (or non-abstract for that matter) inheritance in Grails.
Very quickly, my inheritance is as follows:
abstract BaseClass { ... }
SomeClass extends BaseClass { ... }
SomeOtherClass extends BaseClass { ... }
And then in another domain object:
ThirdClass {
...
BaseClass baseProperty
...
}
But now, when I try to set that property to either a SomeClass or SomeOtherClass instance, Grails compains:
ERROR util.JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails
...
Is this not possible?
I have also tried having the base class not be abstract, and also tried casting the SomeClass or SomeOtherClass instances to BaseClass. They generate the same error.
UPDATE
I just checked. It works for the first sub-class that I add. But as soon as I try to add the other sub-class it fails.
In other words:
def prop1 = new ThirdClass(baseProperty: instanceOfSomeClass).save()
works fine. But when I then try and do:
def prop2 = new ThridClass(baseProperty: instanceOfSomeOtherClass).save()
it fails.
UPDATE 2
Further investigation shows that something goes wrong during the table creation process. It correctly adds two foreign keys to the ThirdClass table, but the keys incorrectly references:
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `base_class` (`id`),
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `some_class` (`id`)
Don't know why it's choosing the base class and one of the sub-classes? I have tried cleaning etc.
First of all, create your BaseClass outside domain structure. It must be an external class, put it on script folder, source folder.
package com.example.model
/**
* #author Inocencio
*/
class BaseClass {
Date createdAt = new Date()
}
Now, create a regular domain class and extend it.
package com.example.domain
import com.example.model.BaseClass
class SomeClass extends BaseClass {
String name
static constraints = {
name(nullable: false)
}
}
As you can see, once you persist SomeClass a createdAt field is filled and saved as well. Check the test class out:
#TestFor(SomeClass)
class SomeClassTests {
void testSomething() {
def some = new SomeClass()
some.name = "Hello There"
some.save()
//find it
def someFind = SomeClass.list()[0]
assert someFind
assertTrue someFind.createdAt != null
// println "Date: $someFind.createdAt"
// println "Name: $someFind.name"
}
}
I hope it can be helpful.
I have just created class structure as yours (Grails 2.1.0) and there is no problem. It works when mocked and unit-tested. The same when scaffolded and SomeClass and ThirdClass instances saved from forms.
Try clean your DB, especially if you haven't used 'create-drop' mode. Maybe there is some old constraint left.
Last thing, you haven't specified when the error occurs - on save (create or update)? It's rather not probable to get JDBC exception on property set, is it?
I don't remember for sure but it's possible that simple property isn't cascaded by default then try to save SomeClass instance before saving the ThirdClass instance. Also you can auto-cascade instead of declaring simple property by use hasOne relation like:
class ThirdClass {
...
static hasOne = [baseProperty:BaseClass]
}