How to access url mapping parameters within a constraint custom validation? - grails

In a grails 2.2.3 app I have the following url-mapping
import app.UserService
class UrlMappings {
UserService userService
static mappings = { ctx ->
"/users/$id/records/$school/detail" {
controller = 'user'
action = 'recordsDetail'
constraints {
id matches: /\d+/
school validator: { school, obj -> school in ctx.userService.getUserSchools(id) }
}
}
}
}
I need to implement a custom validation in school parameter. In order to do it I need to call userService.getUserSchools(id) method.
The current try give java.lang.NullPointerException with the message Cannot get property 'id' on null object I need to get access to id parameter within school custom validator. I tried id or obj.id but with no success.
Thanks for any advice

obj in this case is RegexUrlMapping which doen't allow to access id parameter, but you can use org.springframework.web.context.request.RequestContextHolder to access any request parameters inside validator closure as follows:
RequestContextHolder.requestAttributes.params.id
or just:
RequestContextHolder.requestAttributes.id

Related

Grails Controller Action Abstract Command Object Parameter

Is there any support for using abstract command objects in controller action parameters? Then depending on the given parameters in a JSON request it would select the correct command object?
For example something like:
class SomeController {
def someAction(BaseCommand cmd){
// cmd could be instance of ChildCommandOne or ChildCommandTwo
}
class BaseCommand {
String paramOne
}
class ChildCommandOne extends BaseCommand {
String paramTwo
}
class ChildCommandTwo extends BaseCommand {
String paramThree
}
}
As of now I've been using request.JSON to detect the passed in parameters and instantiate the correct Command object. Is that my only option to handle this sort of case?
EDIT :
To clarify the use case here. I have two domain models that share the same base class domain model and I'm modeling the inheritance in the database using the default table-per-hierarchy model.
In my case, one of the child domain models Model A requires a non-nullable String called body, that is a Text entry, while the other Model B requires a non-nullable String called directUrl. These represent announcements that can be made on the platform. Model A being a write in entry that contains the announcement body while Model B represents a link to a third party site that contains the actual announcement.
In these sort of scenarios I've traditionally put an if statement in the controller action that determines which related command object to instantiate but I am hoping for a cleaner method.
It won't work this way. Grails needs a concrete class (with default public constructor) to bind request params to a command object instance. Therefore this class is to be defined explicitely as action's argument.
I guess you will have to call binding manually depending on what map contains.
See RootModel.from(Map map). In your case Map would be params from Controller
import static com.google.common.base.Preconditions.checkNotNull
import spock.lang.Specification
import spock.lang.Unroll
class CommandHierarchySpec extends Specification {
#Unroll
def "should create object of type #type for map: #map"() {
when:
def modelObj = RootModel.from(map)
then:
modelObj.class == type
where:
type | map
ModelA | [body: 'someBody', test: 'test']
ModelB | [directUrl: 'directUrl', test: 'test']
}
def "should throw ISE when map does not contain neither body nor url"() {
when:
RootModel.from(a: 'b')
then:
thrown(IllegalStateException)
}
}
abstract class RootModel {
static RootModel from(Map map) {
checkNotNull(map, "Parameter map mustn't be null")
RootModel rootModel
if (map.body) {
rootModel = new ModelA()
} else if (map.directUrl) {
rootModel = new ModelB()
} else {
throw new IllegalStateException("Cannot determine command type for map: $map")
}
map.findAll { key, value -> rootModel.hasProperty(key) }
.each {
rootModel.setProperty(it.key, it.value)
}
rootModel
}
}
class ModelA extends RootModel {
String body
}
class ModelB extends RootModel {
String directUrl
}

Grails data binding field exclusion

I am using Grails 2.5 and use Grails databinding in request methods.
For a basic example of the situation consider the following:
Domain class
class Product {
String field1
String privateField
}
Controller
class ProductController {
def update(Product productInstance) {
productInstance.save()
}
}
If I pass an existing Product to the controller like
{"id":3, "privateField":"newValue","field1":"whatever"}
the old value of privateField is overwritten. I want to enforce, that privateField is never bound from a request and avoid checking if the field is dirty.
Is there a mechanism in Grails to achieve this?
If I have to do the dirty check, how can I discard the new value and use the old one?
Pretty sure there's a "bindable" constraint.
http://grails.github.io/grails-doc/2.5.x/ref/Constraints/bindable.html
class Product {
String field1
String privateField
static constraints = {
privateField bindable: false
}
}
Should keep that field from binding automatically.
You can enforce which values are bound, but you'll need to change your method signature to get more control of the data binding process.
class ProductController {
def update() {
def productInstance = Product.get(params.id)
bindData(productInstance, params, [exclude: ['privateField']]
productInstance.save()
}
}

How can I use path variables in grails controller?

I have been trying to use a path variable in grails controller but I am not able to achieve it.
The intention behind is to validate the parameter submitted to the url which I need to make mandatory. I could not achieve it through RequestParam so I switched to PathVariable so that the url submitted without the required param should be filtered off by grails controller itself rather than me adding if/else checks for validity.
So, I can illustrate as below:
My URL is something as below:-
'<appcontext>/<controller>/<action>?<paramName>=<something>'
Now, to make 'paramName' mandatory I am not finding any way in Grails(Spring MVC provides #RequestParam annotation which can enable me for 'required' as true).
Another alternative I thought was to use path variables so that 'paramName' can be included in URL itself. So I tried like following:
'<appcontext>/<controller>/<action>/$paramName'
For validating the above URL I wrote specific mapping but some how it does not work too..
Following is the specific mapping I wrote:-
"/<controllerName>/<action>/$paramName" {
controller:<controller to take request>
action:<action to do task>
constraints {
paramName(nullable: false,empty:false, blank: false)
}
}
I tried to use spring annotation like #PathVariable and #RequestParam in controller as given below:-
def action(#PathVariable("paramName") String param){
//code goes here
}
If you name the method argument the same as the request parameter rename, Grails will take care of it for you...
// In UrlMappings.groovy
"/foo/$someVariable/$someOtherVariable" {
controller = 'demo'
action = 'magic'
}
Then in your controller:
// grails-app/controllers/com/demo/DemoController.groovy
class DemoController {
def magic(String someOtherVariable, String someVariable) {
// a request to /foo/jeff/brown will result in
// this action being invoked, someOtherVariable will be
// "brown" and someVariable will be "jeff"
}
}
I hope that helps.
EDIT:
Another option...
If for some reason you want different names for the method arguments you can explicitly map a method argument to a request parameter like this...
import grails.web.RequestParameter
class DemoController {
def magic(#RequestParameter('someVariable') String s1,
#RequestParameter('someOtherVariable') String s2) {
// a request to /foo/jeff/brown will result in
// this action being invoked, s2 will be
// "brown" and s1 will be "jeff"
}
}

How to restrict properties for a domain being set from params

I've got several properties in my domain class. However, I only want few of them to be set via the params object. What is a good way to do this?
Example:
Domain
class Color {
String name
String shade //don't want this set by params
}
controller
class ColorController {
def save() {
json {
def c = new Color(params?.color)
c.save(flush: true)
//..more code
}
}
}
If someone sends a request like:
{"color":
{name: "red",
shade: "light"
}
}
then user can change the shade property. How can I stop this?
You could probably do one of a couple of things:
If it is many properties, create a transient beforeInsert() {} and/or transient beforeUpdate() {} method in your domain class and handle setting (or not) the properties.
If only a few, override the setters in the domain class.
Since Groovy makes me not want to mess with getters and setters unless I absolutely have to, I usually use the beforeInsert and beforeUpdate methods.
Grails provides a bindData method on the controller to give you fine grained control of data-binding. For your example you could write this as:
class ColorController {
def save() {
json {
def c = new Color()
bindData(c, params, [include: 'name'])
c.save(flush: true)
//..more code
}
}
}
In this case, only the 'name' field would be set on the c instance before attempting to save.
If you want to to additional validation on the incoming params, I would also suggest looking into using a Command Object for the data binding.

Set Inner Dependency by Type using Structuremap

I have a structuremap configuration that has me scratching my head. I have a concrete class that requires a interfaced ui element which requires an interfaced validation class. I want the outer concrete class to get the default ui element, but get a concrete-class-specific validation object. Something like this:
class MyView
{
IPrompt prompt
}
class GenericPrompt : IPrompt
{
IValidator validator
}
class MyValidator : IValidator
{
bool Validate() {}
}
How can I configure structuremap with the Registry DSL to only use MyValidator when creating dependencies for MyView. (And assumedly using BobsValidator when creating dependencies for BobsView)
Are you getting MyView (and BobsView) from the container? Can we assume that they will all take an instance of IPrompt?
One approach would be to register all of your validators with a name that matches the names of your view. You could implement your own type scanner that just removes the Validator suffix:
public class ValidatorScanner : ITypeScanner
{
public void Process(Type type, PluginGraph graph)
{
if (!typeof (IValidator).IsAssignableFrom(type)) return;
var validatorName = type.Name.Replace("Validator", "");
graph.AddType(typeof(IValidator), type, validatorName);
}
}
Now, if you assume an IPrompt will always be requested by a View that follows that naming convention, your registry could look like:
public class ValidatorRegistry : Registry
{
public ValidatorRegistry()
{
Scan(scan =>
{
scan.TheCallingAssembly();
scan.With<ValidatorScanner>();
});
ForRequestedType<IPrompt>().TheDefault.Is.ConstructedBy(ctx =>
{
var viewName = ctx.Root.RequestedType.Name.Replace("View", "");
ctx.RegisterDefault(typeof(IValidator), ctx.GetInstance<IValidator>(viewName));
return ctx.GetInstance<GenericPrompt>();
});
}
}
To retrieve your view with the appropriate validator, you would have to request the concrete type:
var view = container.GetInstance<MyView>();
Note that this will only work if you are retrieving your view with a direct call to the container (service location), since it depends on the "Root.RequestedType". Depending on how you plan to get your views, you might be able to walk up the BuildStack looking for a View (instead of assuming it is always Root).

Resources