I am working with spring-data-neo4j and i finally made auditing in my project.
This is my config for audit
#Configuration
#EnableNeo4jAuditing
class AuditingConfig {
#Bean
fun auditorProvider(): AuditorAware<Long> = SpringSecurityAuditAwareImpl()
}
class SpringSecurityAuditAwareImpl : AuditorAware<Long> {
override fun getCurrentAuditor(): Optional<Long> {
val authentication: Authentication? = SecurityContextHolder.getContext().authentication
if(authentication?.isAuthenticated != true ||
authentication is AnonymousAuthenticationToken)
return Optional.empty()
val userPrincipal = authentication.principal as UserPrincipal
return Optional.ofNullable(userPrincipal.id)
}
}
and this is my audit class
#JsonIgnoreProperties(
value = ["createdAt", "updatedAt"],
allowGetters = true
)
abstract class DateAudit : Serializable {
#CreatedDate
val createdAt: LocalDateTime? = null
#LastModifiedDate
val updatedAt: LocalDateTime? = null
}
It work perfectly when entity make first.
But when update entity, the "createdAt" property is null.
I know the #CreatedDate is just work when entity created.
After create, it set null .
In JPA, this problem can be avoided by #Column(updatable = false).
So, I want to know spring-data-neo4j has annotation like #Column(updatable = false)
or solution avoid this problem.
You should init the "createdAt" property
Related
How can I exclude the child domain property when I use the grailsWebDataBinder?
For example, I have domains:
class Car {
String carPropertyToExclude
Set<Detail> details
static hasMany = [details: Detail]
}
class Detail {
String detailPropertyToExclude
static belongsTo= [car: Car]
}
I want to exclude the detailPropertyToExclude from Detail when I call the bind method of grailsWebDataBinder and give the car instance as a parameter
Code:
List blackList = ["carPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null, blackList)
Note:
Don't suggest the bindable: false or variants when excluded from anywhere. Only need to know is there a way to do it by providing blackList as bind() method parameter.
These variants also not working:
List blackList = ["carPropertyToExclude", "details.detailPropertyToExclude"]
List blackList = ["carPropertyToExclude", [Detail.class : "detailPropertyToExclude"]]
The main question is how to prepare the blackList to exclude also child's property?
blacklist parameter supports only direct object properties
you can use DataBindingListener
import grails.databinding.events.DataBindingListenerAdapter
class BlackListener extends DataBindingListenerAdapter{
List<String> list
//returns false if you want to exclude property from binding
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
return !list.contains("${obj?.class.name}.${propertyName}".toString())
}
}
...
List blackList = ["Car.carPropertyToExclude", "Details.detailPropertyToExclude"]
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params),
new BlackListener(list:blackList) )
UPD:
Unfortunately the method above does not work with Collection binding.
The problem that SimpleDataBinder.setPropertyValue(...) method loses listener when processing a list.
Not sure if following workaround is good (potentially context initialization required)
but it's possible to register converter for each black list:
import grails.databinding.SimpleDataBinder
import grails.databinding.SimpleMapDataBindingSource
import grails.databinding.converters.ValueConverter
SimpleDataBinder setBlackList(SimpleDataBinder binder, Map<Class,List<String>> blackLists) {
blackLists.each { Class clazz, List<String> blackList ->
def vc = new ValueConverter(){
boolean canConvert(Object value){
return value instanceof Map
}
Object convert(Object value){
def obj = clazz.newInstance()
binder.bind( obj, new SimpleMapDataBindingSource(value), [], blackList )
return obj
}
Class<?> getTargetType(){ clazz }
}
binder.registerConverter(vc)
}
return binder
}
...
Map blackLists = [
(Car.class) : ["carPropertyToExclude"],
(Detail.class) : ["detailPropertyToExclude"]
]
setBlackList(grailsWebDataBinder,blackLists)
...
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params), null,
blackLists[car.getClass()] )
PS: as alternative possible to set grailsWebDataBinder.conversionService...
In a controller you can exclude props from binding by:
def someAction(){
Car car = new Car()
bindData car, params, [exclude: ['carPropertyToExclude', 'details']]
car.details = params.list('details').collect{
bindData new Detail(), [exclude: ['detailPropertyToExclude']]
}
}
You might also want to use the command-objects to represent your form-data.
I have found one solution based on daggett answer. Maybe in greater versions, the bug is fixed or will be fixed. The bug is that when we give the listener as a parameter of bind method for child domains the listener isn't triggered but when we set it as class level listener works. Grails version 3.2.11.
I have created the BlackListener like this:
public class BlackListListener extends DataBindingListenerAdapter {
private final Map<Class<?>, Collection<String>> blackList;
public BlackListListener(Map<Class<?>, Collection<String>> blackList) {
this.blackList = blackList;
}
public Boolean beforeBinding(Object obj, String propertyName, Object value, Object errors) {
Boolean result = Boolean.TRUE;
Collection<String> list = blackList.get(obj.getClass());
if (CollectionUtils.isNotEmpty(list)) {
result = !list.contains(propertyName);
}
return result;
}
}
Then I make my own grailsWebDatabinder bean as prototype:
<bean id="webDataBinder" class="grails.web.databinding.GrailsWebDataBinder" c:_0-ref="grailsApplication"
scope="prototype"/>
<bean id="carBinder" class="CarDataBinder" c:_0-ref="webDataBinder"/>
and then when I want to use data binder I inject the webDataBinder and init listener:
public CarDataBinder(GrailsWebDataBinder grailsWebDataBinder) {
this.grailsWebDataBinder = grailsWebDataBinder;
DataBindingListener blackListListener = new BlackListListener(
ImmutableMap.of(
Car.class, ImmutableSet.of("carPropertyToExclude"),
Detail.class, ImmutableSet.of("detailPropertyToExclude")
)
);
grailsWebDataBinder.setDataBindingListeners(blackListListener);
}
and then:
void bindData(Car car, Map<?, ?> params) {
grailsWebDataBinder.bind(car, new SimpleMapDataBindingSource(params));
}
If there are better ways you can post.
I am trying to clear out the data from the userDeptses set but when I call the clear() method and try to save I get the following error
Caused by: java.lang.UnsupportedOperationException: queued clear cannot be used with orphan delete
I have the cascading set properly, I have even tried using just delete-orphan but still have issues. All relevant classes have equals and hashCode methods implemented: AppSystemUser UserDepts Department
All documentation and articles I have read online say that using the combination of clear() and all-delete-orphan is supposed to work, but not for me. Any help is greatly appreciated.
Grails 3.1.4
Controller:
AppSystemUser user = AppSystemUser.findBySystemUserid(cmd.netid);
user.userDeptses.clear();
userMgmtService.saveAppSystemUser(user);
AppSystemUser:
class AppSystemUser {
String systemUserid
String email
String fullName
Date lastLogin
Boolean active
Set appSystemUserRoles = [];
Set userCollegeses = [];
Set userDeptses = [];
static hasMany = [appSystemUserRoles: AppSystemUserRole,
applicationExtensions: ApplicationExtension,
userCollegeses: UserColleges,
userDeptses: UserDepts]
static mapping = {
version false
fullName column: 'fullName'
lastLogin column: 'lastLogin'
id name: "systemUserid", generator: "assigned"
appSystemUserRoles cascade: "save-update, all-delete-orphan"
userCollegeses cascade: "save-update, all-delete-orphan"
userDeptses cascade: "save-update, all-delete-orphan"
}
....
UserDept:
class UserDepts {
Boolean active
AppSystemUser appSystemUser
Department department
static belongsTo = [AppSystemUser, Department]
static mapping = {
version false
appSystemUser column: "system_userid"
}
....
UserMgmtService:
#Transactional
class UserMgmtService {
def saveAppSystemUser(AppSystemUser user) {
user.save();
}
}
I was not able to find out how to successfully use the clear() method of the Set so I just created a simple workaround that does the trick
def static hibernateSetClear(Set data) {
if(data) {
Iterator i = data.iterator();
while (i.hasNext() && i.next()) {
i.remove();
}
}
}
Just iterate through the Set and remove each item individually. This works perfect and I just call this method instead of clear() whenever I need to clear a Set
I am using this Domain Class :
#XmlRootElement(name="Target")
#XmlAccessorType(XmlAccessType.NONE )
class TargetElement {
static constraints = {
testPurpose()
}
#XmlAttribute(name="TestPurpose")
String testPurpose
// instead of bellow in comment use this
#XmlElementWrapper(name = "Bag")
#XmlElement(name="Child")
List<Child> bag= new ArrayList<Child>()
// 1..n Child
static hasMany = [child:Child]
}
If I want to add Child elem, I use
def target = new TargetElement(testPurpose:"TestPurpose")
target.getBag().add(child)
target.save(flush:true)
if (target.hasErrors()){
println '3 ***********' + target.errors
}
But I get list == null for bag.
Why my list is null? It is working for another project and only name is changing. May be my eyes are missing something...
For whom who get the same trouble : having #XmlElementWrapper value(bag) to null
in my Bootstrap.groovy.I have to declare :
ArrayList<Child> bag = new ArrayList<Child>()
bag.add(child1)
bag.add(child2)
targetElement.getBag().addAll(bag)
also I have met another issue whith GROM lazy loading and JAXB. Check that objects nested are retreived :
eg :
static mapping = {
nested lazy: false
}
Hope this will helps
Is there any way I can override the value of dateCreated field in my domain class without turning off auto timestamping?
I need to test controller and I have to provide specific domain objects with specific creation date but GORM seems to override values I provide.
Edit
My classes look like this:
class Message {
String content
String title
User author
Date dateCreated
Date lastUpdated
static hasMany = [comments : Comment]
static constraints = {
content blank: false
author nullable: false
title nullable: false, blank: false
}
static mapping = {
tablePerHierarchy false
tablePerSubclass true
content type: "text"
sort dateCreated: 'desc'
}
}
class BlogMessage extends Message{
static belongsTo = [blog : Blog]
static constraints = {
blog nullable: false
}
}
I'm using console to shorten things up. The problem which I encountered with Victor's approach is, when I write:
Date someValidDate = new Date() - (20*365)
BlogMessage.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
I get following exception:
groovy.lang.MissingFieldException: No such field: dateCreated for class: pl.net.yuri.league.blog.BlogMessage
When I tried
Message.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
Script goes well, but unfortunately dateCreated is not being altered.
I was having a similar issue, and was able to overwrite dateCreated for my domain (in a Quartz Job test, so no #TestFor annotation on the Spec, Grails 2.1.0) by
Using the BuildTestData plugin (which we use regularly anyway, it is fantastic)
Double-tapping the domain instance with save(flush:true)
For reference, my test:
import grails.buildtestdata.mixin.Build
import spock.lang.Specification
import groovy.time.TimeCategory
#Build([MyDomain])
class MyJobSpec extends Specification {
MyJob job
def setup() {
job = new MyJob()
}
void "test execute fires my service"() {
given: 'mock service'
MyService myService = Mock()
job.myService = myService
and: 'the domains required to fire the job'
Date fortyMinutesAgo
use(TimeCategory) {
fortyMinutesAgo = 40.minutes.ago
}
MyDomain myDomain = MyDomain.build(stringProperty: 'value')
myDomain.save(flush: true) // save once, let it write dateCreated as it pleases
myDomain.dateCreated = fortyMinutesAgo
myDomain.save(flush: true) // on the double tap we can now persist dateCreated changes
when: 'job is executed'
job.execute()
then: 'my service should be called'
1 * myService.someMethod()
}
}
Getting a hold of the ClosureEventListener allows you to temporarily disable grails timestamping.
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener
class FluxCapacitorController {
def backToFuture = {
changeTimestamping(new Message(), false)
Message m = new Message()
m.dateCreated = new Date("11/5/1955")
m.save(failOnError: true)
changeTimestamping(new Message(), true)
}
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsAnnotationConfiguration configuration = applicationContext.getBean("&sessionFactory").configuration
ClosureEventTriggeringInterceptor interceptor = configuration.getEventListeners().saveOrUpdateEventListeners[0]
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
}
There may be an easier way to get the applicationContext or Hibernate configuration but that worked for me when running the app. It does not work in an integration test, if anyone figures out how to do that let me know.
Update
For Grails 2 use eventTriggeringInterceptor
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ClosureEventTriggeringInterceptor closureInterceptor = applicationContext.getBean("eventTriggeringInterceptor")
HibernateDatastore datastore = closureInterceptor.datastores.values().iterator().next()
EventTriggeringInterceptor interceptor = datastore.getEventTriggeringInterceptor()
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
I got this working by simply setting the field. The trick was to do that after the domain object has been saved first. I assume that the dateCreated timestamp is set on save and not on object creation.
Something along these lines
class Message {
String content
Date dateCreated
}
// ... and in test class
def yesterday = new Date() - 1
def m = new Message( content: 'hello world' )
m.save( flush: true )
m.dateCreated = yesterday
m.save( flush: true )
Using Grails 2.3.6
As of Grails 3 and GORM 6 you can tap into AutoTimestampEventListener to execute a Runnable that temporarily ignores all or select timestamps.
The following is a small snippet I use in my integration tests where this is necessary:
void executeWithoutTimestamps(Class domainClass, Closure closure){
ApplicationContext applicationContext = Holders.findApplicationContext()
HibernateDatastore mainBean = applicationContext.getBean(HibernateDatastore)
AutoTimestampEventListener listener = mainBean.getAutoTimestampEventListener()
listener.withoutTimestamps(domainClass, closure)
}
Then in your case you could do the following:
executeWithoutTimestamps(BlogMessage, {
Date someValidDate = new Date() - (20*365)
BlogMessage message = new BlogMessage()
message.dateCreated = someValidDate
message.save(flush: true)
})
I'm using something like this for an initial import/migration.
Taking gabe's post as a starter (which didn't work for me Grails 2.0), and looking at the old source code for ClosureEventTriggeringInterceptor in Grails 1.3.7, I came up with this:
class BootStrap {
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
Mapping m = GrailsDomainBinder.getMapping(domainObjectInstance.getClass())
m.autoTimestamp = shouldTimestamp
}
def init = { servletContext ->
changeTimestamping(new Message(), false)
def fooMessage = new Message()
fooMessage.dateCreated = new Date("11/5/1955")
fooMessage.lastUpdated = new Date()
fooMessage.save(failOnError, true)
changeTimestamping(new Message(), true)
}
}
You can try to disable it by setting autoTimestamp = false in the domain class mapping. I doubt about global overriding because the value is taken directly from System.currentTimeMillis() (I'm looking at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.java).
So I can only suggest that you override a setter for dateCreated field in your class, and assign your own value. Maybe even metaclass access will work, like
Date stubDateCreated
...
myDomainClass.metaClass.setDateCreated =
{ Date d -> delegate.#dateCreated = stubDateCreated }
I couldn't get the above techniques to work, the call to GrailsDomainBinder.getMapping always returned null???
However...
You can use the fixtures plugin to set the dateCreated property on a domain instance
The initial loading will not do it...
fixture {
// saves to db, but date is set as current date :(
tryDate( SomeDomain, dateCreated: Date.parse( 'yyyy-MM-dd', '2011-12-25') )
}
but if you follow up with a post handler
post {
// updates the date in the database :D
tryDate.dateCreated = Date.parse( 'yyyy-MM-dd', '2011-12-01')
}
Relevant part of the fixtures docs here
AFAIK fixtures don't work for unit testing, although the plugin authors may add unit testing support in the future.
A simpler solution is to use a SQL query in your integration test to set it as you please after you initialize your object with the other values you want.
YourDomainClass.executeUpdate(
"""UPDATE YourDomainClass SET dateCreated = :date
WHERE yourColumn = :something""",
[date:yourDate, something: yourThing])
As of grails 2.5.1, getMapping() method of GrailsDomainBinder class is not static,non of the above method works as is. However, #Volt0's method works with minor tweaking. Since all of us are trying to do so to make our tests working, instead of placing it in BootStrap, I placed it in actual integration test. Here is my tweak to Volt0's method:
def disableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = false
}
def enableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = true
}
And simply call these methods in tests like
disableAutoTimestamp(Domain.class)
//Your DB calls
enableAutoTimestamp(Domain.class)
The above code can also be placed in src directory and can be called in tests however I placed this in actual test as there was only one class in my app where I needed this.
The easy solution is to add a mapping:
static mapping = {
cache true
autoTimestamp false
}
I am developing a grails application.In that some cases I want to control the domain class fields based on the role.So that in each call to getter setter method of domain class I want to apply some filter based on role(Logged in user's role).I am assuming that grails will create getter setter method at runtime for the domin classes.So while writing grails code is it possible to apply this logic.If it is possible then how to apply?
Example:
Domain Class :
class Book{
String name;
double price;
}
Controller:
def index={
Book book=Book.get(1);
println book.name;
println book.price;
}
In the above code "println book.price;" this line should work only for particular role.For some other role it should throw some exception.
Is it possible achieve?Is there any plugin to do this?
Please give some help on this....Thanks
You can create get/set methods for the properties you want to control access to and put your security logic there. Assuming you've written your own security service or are using a security plugin like the Spring Security (Acegi) plugin you would:
class Book{
String name;
double price;
def authenticateService
void setPrice(double price) {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to set book prices")
}
this.price = price
}
double getPrice() {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to get book prices")
}
return this.price
}
}
I am not aware of any plugin that allows access controls to be put on domain properties.
You could also consider using a custom validator or a spring errors object to catch attempts to set a field before saving it.
EDIT: Here is an example of what I was thinking. You could generalize quite a bit more and the code here hasn't been tested so it probably won't run as is.
class securedDomain {
String securedField
def fieldSetBy = [:]
def previousValue = [:]
static transients = ['fieldSetBy', 'previousValue']
static constraints = {
securedField(validator: { v, o ->
def access = User.findByName(fieldSetBy['securedField']).hasAccess('securedField')
if(!access) securedField = previousValue['securedField']
return access
})
void setProperty(String name, value) {
if(name == "securedField") {
fieldSetBy['securedField'] = session.user
previousValue['securedField'] = securedField
securedField = value
} else {
super(name, value)
}
}