Grails binddata in service - grails

Is there a way to utilize bindData in a service other than using the deprecated BindDynamicMethod? I can't just use
TestObject testObject = new TestObject()
TestObject testObject.properties = params
or
TestObject testObject = new TestObject(params)
because I have a custom bind method utilizing the #BindUsing annotation within my TestObject class.

If you are using Grails 3.* then the service class can implement DataBinder trait and implement bindData() as shown below example:
import grails.web.databinding.DataBinder
class SampleService implements DataBinder {
def serviceMethod(params) {
Test test = new Test()
bindData(test, params)
test
}
class Test {
String name
Integer age
}
}
This is how I quickly tried that in grails console:
grailsApplication.mainContext.getBean('sampleService').serviceMethod(name: 'abc', age: 10)

In Grails 2.4.4 you can do something like this:
// grails-app/services/demo/HelperService.groovy
package demo
import org.grails.databinding.SimpleMapDataBindingSource
class HelperService {
def grailsWebDataBinder
TestObject getNewTestObject(Map args) {
def obj = new TestObject()
grailsWebDataBinder.bind obj, args as SimpleMapDataBindingSource
obj
}
}

In 2.5, I found that emulating the behaviour of the Controller API in a helper service worked:
def bindData(def domainClass, def bindingSource, String filter) {
return bindData(domainClass, bindingSource, Collections.EMPTY_MAP, filter)
}
def bindData(def domainClass, def bindingSource, Map includeExclude, String filter) {
DataBindingUtils
.bindObjectToInstance(
domainClass,
bindingSource,
convertToListIfString(includeExclude.get('include')),
convertToListIfString(includeExclude.get('exclude')),
filter);
return domainClass;
}
convertToListIfString is as per the controller method:
#SuppressWarnings("unchecked")
private List convertToListIfString(Object o) {
if (o instanceof CharSequence) {
List list = new ArrayList();
list.add(o instanceof String ? o : o.toString());
o = list;
}
return (List) o;
}

Related

Grails, GORM - No dirty properties when binding OneToMany data

I have a problem with data binding and dirty properties.
There are two simple domains
class Parent {
String name
static hasMany = [children: Child]
}
class Child {
String name
static belongsTo = [parent: Parent]
}
I created a simple rest controller to check all options
class TestController {
static responseFormats = ['json']
#Transactional
def add() {
def parent = new Parent(name: params.name).save()
respond(parent)
}
#Transactional
def parents() {
def parents = Parent.findAll()
respond(parents)
}
#Transactional
def testWithCommand(ParentCmd cmd) {
Parent parent = Parent.findById(cmd.id)
bindData(parent, cmd)
parent.save()
respond(parent)
}
#Transactional
def testWithParams() {
Parent parent = Parent.findById(params.id)
bindData(parent, params)
parent.save()
respond(parent)
}
#Transactional
def testWithRelation(ParentCmd cmd) {
Parent tempParent = new Parent()
bindData(tempParent, cmd)
Parent parent = Parent.findById(cmd.id)
for (Child child : tempParent.children) {
parent.addToChildren(child)
}
parent.save()
respond(parent)
}
}
so we have 3 update options: cmd with binding, params with binding and cmd with addTo*
I created simple event to catch dirty properties
class TestEventListener extends AbstractPersistenceEventListener {
protected TestEventListener(Datastore datastore) {
super(datastore)
}
#Override
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
if (event.entityObject instanceof Parent) {
List<String> dirtyProperties = new ArrayList<String>(((DirtyCheckable) event.entityObject).listDirtyPropertyNames())
println("DIRTY PROPERTIES: " + dirtyProperties.size())
println(dirtyProperties.join(", "))
}
}
#Override
boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
eventType == PreUpdateEvent.class
}
}
Then I used Postman, I created one record and updated it in each of three ways.
In two cases there was no dirty properties.
Only when I used addToChildren() I saw children in dirty properties.
Does anyone have any knowledge about this?
Did you have problems with binding? Is it normal or did I miss something?
I will be grateful for every answer.
Thx,
A.

grails restful groovy can not resolve symbol

I am trying to learn grails by following the link: https://guides.grails.org/rest-hibernate/guide/index.html and after creating the controller ProductController as per the page's guidance I am getting the following error:
Unable to resolve class ProductService in ProductController
I new to groovy and trying to resolve the class reference by importing the necessary packages but the link does not show any imports to resolve this class. there is not explicit import statement for ProductService productService in ProductController.groovy. Here are the classes:
ProductController:
package hibernate.example
import groovy.transform.CompileStatic
import grails.rest.*
import grails.converters.*
#CompileStatic
class ProductController extends RestfulController {
static responseFormats = ['json', 'xml']
ProductController() {
super(Product)
}
ProductService productService
def search(String q, Integer max ) {
if (q) {
respond productService.findByNameLike("%${q}%".toString(), [max: Math.min( max ?: 10, 100)])
}
else {
respond([])
}
}
}
ProductControllerSpec:
package hibernate.example
import org.grails.testing.GrailsUnitTest
import spock.lang.Specification
#SuppressWarnings('MethodName')
class ProductControllerSpec extends HibernateSpec implements ControllerUnitTest<ProductController> {
def setup() {
}
def cleanup() {
}
static doWithSpring = {
jsonSmartViewResolver(JsonViewResolver)
}
void 'test the search action finds results'() {
given:
controller.productService = Stub(ProductService) {
findByNameLike(_, _) >> [new Product(name: 'Apple', price: 2.0)]
}
when: 'A query is executed that finds results'
controller.search('pp', 10)
then: 'The response is correct'
response.json.size() == 1
response.json[0].name == 'Apple'
}
}

Why doesn't this generic usage work in groovy?

Learning Groovy and Grails and I am trying to simplify some controllers by making a BaseController.
I define the following:
class BaseController<T> {
public def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond T.list(params), model:[instanceCount: T.count()]
}
}
Then I have the following:
class TeamController extends BaseController<Team> {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
/*
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Team.list(params), model:[teamInstanceCount: Team.count()]
}
*/
}
Every time I try to make a call to this, I get a MethodMissingException on T.count(). Why does Team.count() work, but when I try to use generics T.count() fail?
-- edit -- (Adding exception)
No signature of method: static java.lang.Object.count() is applicable for argument types: () values: []
T is a type. So this does not work as you expect it to be. You have to hold a concrete class (see Calling a static method using generic type).
So in your case it's easiest to also pass down the real class. E.g.:
class A<T> {
private Class<T> clazz
A(Class<T> clazz) { this.clazz = clazz }
String getT() { T.getClass().toString() }
// wrong! String getX() { T.x }
String getX() { clazz.x }
}
class B {
static String getX() { return "x marks the place" }
}
class C extends A<B> {
C() { super(B) }
}
assert new C().x=="x marks the place"

Nested entities in Grails with HAL renderer

I've got grails domain class that looks like this
class Thing {
String name
static hasMany = [
variants: Variant
]
}
and another one like this
class Variant {
String name
static belongsTo = [
thing: Thing
]
}
I'm trying to get the hal renderer to do a deep rendering.
Is that possible? How should I achieve it?
Same problem here, it seems a known bug in grails (https://jira.grails.org/browse/GRAILS-10954)
There is a (not very nice) workaround,
#Transactional(readOnly = true)
class ProductController extends RestfulController {
def halPCollectionRenderer
def halPRenderer
static responseFormats = ['hal','json']
ProductController() {
super(Product)
}
#PostConstruct
void init() {
halPCollectionRenderer.mappingContext = mappingContext
halPRenderer.mappingContext = mappingContext
}
MappingContext getMappingContext() {
final context = new KeyValueMappingContext("")
context.addPersistentEntity(Product)
context.addPersistentEntity(Category)
return context
}
}
Hope it helps.

Cannot invoke method render() on null object in Grail while email sending

I am using mail:1.0.1 plugin for mail sending
but while sending mail its gives me an error..
Source :-
def serviceMethod(EmailModel mailObj) {
PageRenderer groovyPageRenderer;
try{
sendMail {
to "abc#gmail.com"
subject mailObj.subject;
html groovyPageRenderer.render(template:"myMailTemplate", model: [mailObj: mailObj])
}
} catch (Throwable th) {
th.printStackTrace();
}
}
If you want to send the gsp page as email body then you can send it like:
def mailService
def serviceMethod(EmailModel mailObj) {
...
mailService.sendMail {
to email
subject "subject"
body(view: "/_template", model: [mailObj: mailObj])
}
...
}
EDIT...................................................................................
Just inject PageRenderer groovyPageRenderer globally, like
import grails.gsp.PageRenderer
class TestService {
PageRenderer groovyPageRenderer
def getText() {
String s = groovyPageRenderer.render(template: "../first/temp", model: [name: 'user1690588'])
println "Content = ${s}"
}
}
I think you are calling Service Class(.groovy) method from java class.
by using object of EmailService class.
So you cant get Object of PageRenderer class.
for this
Create SpringsUtil Class in src/java and define constant object of EmailSerevice. like this
public class SpringsUtil {
public static ApplicationContext getCtx() {
return getApplicationContext();
}
public static ApplicationContext getApplicationContext() {
return (ApplicationContext) ServletContextHolder.getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT);
}
#SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
return (T) getApplicationContext().getBean(beanName);
}
public static final String EMAIL_SERVICE = "emailService";
// public static final String INVENTORY_REORDER_SERVICE = "InventoryReorderService";
}
create object of Service class and call method
EmailService emailService = SpringsUtil.getBean(SpringsUtil.EMAIL_SERVICE);

Resources