I am trying to implement the beforUpdate event in grails domain classes I need to audit log both the old and new values of the Domains attributes. I see that we can use the isDirty check or use Domain.dirtyPropertyNames which return list of properties which are dirty in the domain. and getPersistentValue gets the old value in the table so I can have both values..
For implementing this I will be using the beforUpdate event in the domain class and call a logging service from there, passing it the id of the User domain. now using this ID I can get user Instance in Service and then check if any fields are dirty using the above specified method? or do I need to log the Audit when I am actually doing the Update in the UserController's update def?
Which is the better approach?
I want to confirm if this the right approach..
Also what other things I need to take care for, Like:
1) if the attributes are domain object references and not simple types.
2) any other things I need to take care like not flushing out the hibernate session, thinking of implementing this in call to service from the domain class.
Regards,
Priyank
Edit: I tried this in the beforeUpdate event in User domain that I want to Audit log for Update activity..
def beforeUpdate = {
GraauditService service = AH.getApplication().getMainContext().getBean(''graauditService)
service.saveUserUpdateEntry(this.id); // id property of User domain...
}
In the method in Service I do:
def saveUserUpdateEntry(Long id){
User grauser = User.get(id);
println ("user="+ grauser)
println "Dirty Properties -: ${grauser.dirtyPropertyNames}"
println "Changed value for firstName = -: ${ grauser.firstName}"
println "Database value for firstName = -: ${ grauser.getPersistentValue('firstName')}"
}
I try to do the update from the UI for email, firstname, lastname and get the following on console:
user=com.gra.register.User : 1
Dirty Properties -: [eMail, firstName, lastName]
Changed value for firstName = -: sefser
Database value for firstName = -: administer
user=com.gra.register.User : 1
Dirty Properties -: []
Changed value for firstName = -: sefser
Database value for firstName = -: sefser
possible nonthreadsafe access to session
I am not able to know:
1) Why am I getting 2 sets ... is the event called twice once before commit and once after commit...??
2) how to remove or handle the Hibernate exception (tried to use withNew session in the function but no difference
Thanks in Advance..
Rather than using GORM event handlers for audit logging, use audit logging plugin. This will take away a lot of pain of yours.
Hope this helps.
or
If You want much finer control over what you are doing you should consider using a subclass of Hibernate's EmptyInterceptor. This will serve tow purposes for you
Will give you much finer control over what and how you are doing the audit logging
Will place all your logic for audit logging at one place, which will help you maintaining your code.
Click here to see the API for EmptyInterceptor.
Note: Hibernate does not ship any implementation in this class and also don't provide any subclass of this which might provide you the default behavior. So you will have to write a custom implementation.
Related
I am writing a Post PLugin changing the owner. When the owner has a substitution manager, the owner is changed to the substitution manager. I tried a service.Update and an AssignRequest, but these throw an exception.
When I post the request my entity cannot update (and then throws "The request channel time out while waiting for reply after 10:00:00"). But like I see there is no recursion, because when I logged it I have only one repetition of log and it has stopped before or on line with update.
var assignedIncident = new AssignRequest
{
Assignee = substManagerRef, //get it throw another method, alreay checked in test it`s correct
Target = new EntityReference ("incident", incedentId)
};
service.Execute(assignedIncident);
I tried to write target in another way
Target = postEntityImage.ToEntityReference()
I tried to write simple update but the problem is the same.
Entity incident = new Entity("incident" , incidentId);
incident["ownerid"] = substManagerRef:
service.Update(incident);
Can somebody help me with that? Or maybe show the way to solve it)
The plugin is triggering itself and gets in a loop. The system only allows a maximum of 8 calls deep within plugins and that is the reason it throws an error and rolls back the transaction.
To solve this issue redesign your plugin in the following way:
Register your plugin on the Update message of your entity in the PreValidation stage.
In the plugin pick up the Target property from the InputParameters collection.
This is an Entity type. Modify or set the ownerid attribute on this Entity object.
That is all. You do not need to do an update, your modification is now passed into the plugin pipeline.
Note: for EntityReference attributes this technique only works when your plugin is registered in the PreValidation stage. For other regular attributes it also works in the PreOperation stage.
I am starting to work with the Django REST framework for a mini-reddit project I already developed.
The problem is that I am stuck in this situation:
A Minisub is like a subreddit. It has, among others, a field named managers which is ManyToMany with User.
An Ad is an advertising which will be displayed on the minisub, and it has a field named minisubs which is ManyToMany with Minisub. It has also a author field, foreign key with User.
I would like to allow these managers to add some ads on their minisubs through a DRF API. It is actually working. But I want to check that they put in minisubs only minisubs where they are managers.
I found a way like that:
class AdSerializer(serializers.HyperlinkedModelSerializer):
# ...
def validate_minisubs(self, value):
for m in value:
if user not in m.managers.all():
raise serializers.ValidationError("...")
return value
My question is: How to get user ? I can't find a way to get the value Ad.author (this field is set automatically in the serial data according to the user authentication). Maybe I don't find a way because there is no ways ? The place to do this is somewhere else ?
Thanks in advance.
You may get it out of the serializer this way:
class YourModelSeializer(serializers.HyperlinkedModelSerializer):
class Meta:
model=YourModel
def validate_myfield(self):
instance = getattr(self, 'instance', None)
...
I believe that this is a job for the permissions, if you are performing CRUD operations for inserting that into a database then u can have a permission class returns True if the user is a manager.
a permissions instance has access to the request which u can use to get the user and check if he is a manager:
http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions
I have the following in my application:
Action Orm entity (From telerik open access)
Repository(Of Action)
AppService(Holds an instance of the repository)
when I need to save an instance, I send the instance to the AppService. the AppService then calls a validator to validate the instance to save. the validator is based on http://codeinsanity.com/archive/2008/12/02/a-framework-for-validation-and-business-rules.aspx
(full code on https://github.com/riteshrao/ncommon)
so basically my save function in the AppService looks like this
Public Sub AddAction(ByVal Item As Data.Model.Action)
Contract.Requires(Of ArgumentNullException)(Item IsNot Nothing, "Item is nothing.")
Dim validateResult As Rules.ValidationResult = _ActionValidator.Validate(Item)
If Not validateResult.IsValid Then
Throw New Validation.ValidationException(validateResult)
End If
Try
_ActionRepository.Add(Item)
_unitOfWork.SaveChanges()
Catch ex As Exception
_unitOfWork.ClearChanges()
Throw New DataServiceException(ex.Message, ex)
End Try
End Sub
for checking properties of the Action item the sample code works great. my question begins when i need to make sure that the same action is not added twice to the DB for the same customer (ie. id is difference, name is the same and customer is the same)
as I see it I have a few options:
option 1: check for a duplicate action using something like
function(validatedItem) item.Customer.Actions.Any(function(item) item.id<>validatedItem.id andalso item.name=validatedItem.name))
basically I go from the action being saved back to the customer and then back to all his actions and check if any action exists with a different id and same name
the down sides are:
a. for this to work, when accessing the customer property of the item, the entire customer object is read from DB which is redundant in this case
b. the Any function is being evaluated on the client as item.Customer.Actions returns IList(Of Action)
Option 2: let the validation class have access to the action repository. then i could simply do something like
'assume I already have validatedItem
repository.Any(function(item) item.id<>validatedItem.id and item.customerid=validatedItem.customerid and item.name=validatedItem.name)
this will result in an Exists query being sent to the DB but the downside(?) is that the validation framework should not access the repository directly (as far as I have seen in the very few examples i could find that do use validation and ORM)
Option 3: let the validation class have access to the AppService and use the AppService to check for existence of a duplicate.
problems:
a. I create a circular reference (AppService->Validation Class->AppService)
b. I need to create a lot of useless functions in the AppService for loading items based on criteria that is only relevant for the validation
Any ideas what is the best course here?
The simplest is not to check duplicates in the database from your domain.
When a collection of entities is part of you aggregate then it is a non-issue since you would not permit the duplicate to be added to the collection. Since the aggregate is stored as a whole you would never run into the problem.
For scenarios where you do not want a duplicate, say, e-mail address and no collection of the entities is represented by an aggregate (such as the Users in a system) you can just let the database enforce the uniqueness. Simply pick up the exception and report back. In many instances your validation would not be able to enforce the uniqueness simply because it doesn't have/implement the required locks that a database system would have.
So I'd simply leave that up to the database.
I have a form that I have users fill out and then it gets e-mailed to me.
I am trying to get an example of how I would create an ID (based on my own conventions) that I can use to keep track of responses (and send back to the user so they can reference it later).
This is the convention I am striving for:
[YEAR]-[SERVICE CODE]-[DATE(MMDD)]-[TIME]
For example: "2012-ABC-0204-1344". I figured to add the TIME convention in the instance that two different users pick the same service on the same date rather than try to figure out how to only apply it IF two users picked the same service on the same date.
So, the scenario is that after the user goes through my wizards inputting their information and then click "Submit" that this unique ID would be created and attached to the model. Maybe something like #Model.UniqueID so that in an e-mail response I send to the user it shows up and says "Reference this ID for any future communication".
Thanks for any advice/help/examples.
In your post action
[HttpPost]
public ActionResult Create(YourModel model)
{
model.UniqueId = GenerateUniqueId(serviceCode);
}
public string GenerateUniqueId(string serviceCode)
{
return string.Format("{0}-{1}-{2}", DateTime.Now.Year, serviceCode, Guid.NewGuid().ToString().Replace("-",""); //remove dashes so its fits into your convention
}
but this seems as I'm missing part of your question. If you really want unique, use a Guid. This is what we've used in the past to give to customers - a guid or a portion of one. IF you use a portion of one ensure you have logic to handle a duplicate key. You don't need to worry about this though if using a full guid. If the idea is just to give to a customer then ignore the rest of the data and just use a guid, since it can easily be looked up in the database.
The Dynamics AX 2009 Best Practice add-in is throwing the following error on a display method override.
"TwC: Validate access to return value from the display/edit method."
Here is my display method.
display ABC_StyleName lookupModuleName(ABC_StyleSettings _ABC_StyleSettings)
{
;
return ABC_Styles::find(_ABC_StyleSettings.StyleID).StyleName;
}
I'm assuming it wants me to check a config or security key before returning a result. Any suggestions/examples on where to start?
Thanks
This is a reminder that you need to consider whether the user should have access to the data you are returning from the function. For table fields, the kernel normally does this for you based on the security groups the user is in and the security keys set on fields.
To check if a user has access to a field, use the hasFieldAccess function. To see how this is used, look at the table methods BankAccountStatement.openingBalance() or CustTable.openInvoiceBalanceMST(). There are other helper functions to check security keys such as hasMenuItemAccess, hasSecuritykeyAccess, and hasTableAccess.
In your case, add this code:
if(!hasFieldAccess(tablenum(ABC_Styles),fieldnum(ABC_Styles,StyleName)))
{
throw error("#SYS57330");
}
Even after you add that code, you will still get the Best Practice error. To tell the compiler you have addressed the issue, you need to add the following comment immediatly before the function declaration:
//BP Deviation Documented