I'm trying to do some serverside validation in grails and pass my errors back to the frontend as json to be processed by angularjs.
Error conditions
Department - required
Department - unique
Description - foobar not allowed
I have the following code.
Controller
def saveDepartment() {
def errors = []
def success = true
def department
try{
department = departmentService.save(request.JSON);
if(department.hasErrors()) {
success = false
errors = department.errors.fieldErrors;
}
} catch(Exception e){
e.printStackTrace()
errors = "Unknown"
success = false
if(log.errorEnabled){
log.error("save department encountered unknown error: ", e)
}
response.status = 500
} finally {
respond ([success:success, errors:errors, department:department]) as JSON;
}
}
Service
def save(jsonObj) {
def dept = new Department();
dept.setName(jsonObj.name);
dept.setDescription(jsonObj.description);
if(dept.description.equals('foobar')) {
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
if (!dept.save()) {
dept.discard();
}
return dept;
}
Service Method Attempt 2 with debugging code
def save(jsonObj) {
def dept = new Department();
dept.setName(jsonObj.name);
dept.setDescription(jsonObj.description);
if(dept.description.equals('foobar')) {
println 'rejected value '
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
println 'dept errors ' + dept.errors.allErrors.size();
if (dept.errors.hasErrors()) {
dept.errors.allErrors.each {FieldError error ->
println error
}
}
if (!dept.save(true)) {
println 'dept errors 2 ' + dept.errors.allErrors.size();
if (dept.errors.hasErrors()) {
dept.errors.allErrors.each {FieldError error ->
println error
}
}
}
return dept;
}
Output
..................rejected value
dept errors 1
Field error in object 'org.hri.leaverequest.Department' on field 'description': rejected value [foobar]; codes [foobar.org.hri.leaverequest.Department.descripti
on,foobar.description,foobar.java.lang.String,foobar]; arguments []; default message [Foobar is not allowed]
dept errors 2 1
Field error in object 'org.hri.leaverequest.Department' on field 'name': rejected value [null]; codes [org.hri.leaverequest.Department.name.nullable.error.org.h
ri.leaverequest.Department.name,org.hri.leaverequest.Department.name.nullable.error.name,org.hri.leaverequest.Department.name.nullable.error.java.lang.String,or
g.hri.leaverequest.Department.name.nullable.error,department.name.nullable.error.org.hri.leaverequest.Department.name,department.name.nullable.error.name,depart
ment.name.nullable.error.java.lang.String,department.name.nullable.error,org.hri.leaverequest.Department.name.nullable.org.hri.leaverequest.Department.name,org.
hri.leaverequest.Department.name.nullable.name,org.hri.leaverequest.Department.name.nullable.java.lang.String,org.hri.leaverequest.Department.name.nullable,depa
rtment.name.nullable.org.hri.leaverequest.Department.name,department.name.nullable.name,department.name.nullable.java.lang.String,department.name.nullable,nulla
ble.org.hri.leaverequest.Department.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class org.hri.leaverequest.Department]; default mess
age [Property [{0}] of class [{1}] cannot be null]
Issues
If department is null and description has foobar with rejectValue, only one error, "department null" is returned, foobar does not appear in the errors.
If department contains existing value and description contains foobar, the unique constraint is returned but foobar does not appear in the errors.
If department has a good value and foobar still exist, the rejectValue doesn't prevent the save from happening and no errors are thrown. Now if I output dept.errors after the rejectValue, I can see the error actually exist.
Goal
My goal is to return all my errors and not save to the db if an error exist, what am I missing to achieve that goal?
You can do it this way:
dept.validate()
if(dept.description.equals('foobar')) {
dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
}
if(!dept.errors.hasErrors()) {
dept.save()
}
return dept
surely that is the validation constraints that you need to get right in the domain class or relevant validator?.
You have a set of criterias and the validation on the backend should fail to match what you expect to return or not as an error ? or maybe I am missing something
class Example {
Department deparment
static constraints = {
department(nullable:false, blank:false, unique:true, validator: checkDept)
}
static def checkDept= { val, obj, errors ->
//department has a value
if (val) {
//val is now also the same as object.department
if (obj.deparment.description='foo') {
errors.rejectValue(propertyName, "nullable.input", [''] as Object[], 'this is description as foo being rejected')
} else if (obj.deparment.name='bar') {
errors.rejectValue('department.name', "nullable.input", [''] as Object[], 'this is name of bar')
} else {
errors.rejectValue(propertyName, "nullable.input", [''] as Object[], 'this is null and being rejected')
}
}
}
}
propertyName will bind to actual object name - if you are failing based on department.something and the field names are that on the gsp page then you may need to tweak that.
The logics of above is not exactly what you have asked for but it should give you an idea of how you can customise to exactly what you wish to fail. If it doesn't match those then it will just go through as you require
Related
I am checking if my user has entered the same password twice in controller:
if(params.password1 != "" || params.password2 != "") {
// change password
if(params.password1 != params.password2) {
user.errors.rejectValue('password', 'noMatch')
}
user.password = params.password1
}
if (user.errors.errorCount > 0 || !user.save()) {
println user.errors.errorCount
render(view: "edituser", model: [userInstance: user])
return
}
I get the correct user error count displayed in console so my if-clause works and Grails does not re-validate the object by using double pipe.
When I have a look into my GSP I get the following message:
grails.validation.ValidationErrors: 0 errors
It seams like Grails clears all errors or uses an other object. It's curious, because my custom validator in my domain class works fine...
I'm trying to write a simple Geb/Spock test using Grails but I am receiving the following test failure.
| Failure: login works correctly(...UserAuthAcceptanceSpec)
| Condition not satisfied:
at HomePage
|
null
I can follow the test through with a debugger using a browser and can see that the application works as expected and the correct heading is being shown. However, the test is failing when I try to invoke the at checker. Can anyone tell me why the final assertion in the test might be failing and why the 'at' checker appears to be null?
Here is my code: (Geb v0.9.0, Grails 2.2.2)
Spock Specification:
class UserAuthAcceptanceSpec extends GebReportingSpec {
def "login works correctly"() {
given: "the correct credentials"
def theCorrectUsername = "admin"
def theCorrectPassword = "password"
when: "logging in"
to LoginPage
username = theCorrectUsername
password = theCorrectPassword
submitButton.click() //([HomePage, LoginPage])
then: "the welcome page is shown"
heading =~ /(?i)Welcome.*/ // <- same as 'at' checker in HomePage
and: "the 'at' checker works"
at HomePage // <- fails
}
LoginPage:
class LoginPage extends Page {
final String path = "/login/auth"
static content = {
heading(required: false, wait:true) { $("h1").text() }
username { $("input", name:"j_username") }
password { $("input", name:"j_password") }
submitButton { $("input", id:"submit") }
}
static at = {
title =~ /Login.*/
}
}
HomePage:
class HomePage extends Page {
final String path = "/"
static content = {
heading(required: false, wait:true) { $("h1").text() }
}
static at = {
heading =~ /(?i)Welcome.*/
}
}
The at checker should use ==~ rather than =~.
Geb's implicit assertions mean the statements:
heading1 =~ /(?i)Welcome.*/
heading2 ==~ /(?i)Welcome.*/
effectively become:
assert (heading1 =~ /(?i)Welcome.*/) == true // [1]
assert (heading2 ==~ /(?i)Welcome.*/) == true // [2]
[2] will evaluate to a boolean and pass/fail as expected, whereas [1] evaluates to a java.util.regex.Matcher causing the failure.
See the Groovy Regex FAQ for an explanation of the difference between the two syntaxes.
I came accross an interesting problem in my code today.
I am using Grails 2.2.0.
Here is the code
def user = lookupUserClass().get(params.id)
log.info "[update]user.subscriptionExpiryDate1: " + user.subscriptionExpiryDate
user.subscriptionExpiryDate = Calendar.getInstance();
log.info "[update]user.subscriptionExpiryDate2: " + user.subscriptionExpiryDate
if (user.subscriptionExpiryDate instanceof Calendar ) {
log.error "***** Is A Calendar Instance ***"
} else if (user.subscriptionExpiryDate instanceof String ) {
log.error "***** Is A String Instance ***"
} else {
log.error "***** Is Something else ***"
}
if (!user.save()) {
log.error "[update]Error occured saving user. Errors are: "
user.errors.each { err -> log.error err; }
render view: 'edit', model: buildUserModel(user)
return
} else {
log.info "[update]Successfully saved user"
}
subscriptionExpiryDate is a calendar property in my User object.
When I perform the save I get the following error
Failed to convert property value of type 'java.lang.String' to required type 'java.util.Calendar' for property 'subscriptionExpiryDate'; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: 05/03/2013
Could anyone please explain why I would be seeing this error for the above code as nothing is standing out
This question has been resolved as per Andrew's suggestion:
Are you doing any data binding on the user object before the code you pasted? If you are binding to the subscriptionExpiryDate property, you will need a PropertyEditor to do the String -> Calendar conversion
Below is my Geb Page, Spec and Error. I am not sure where and what the issue is. When I remove the below from the ApplicationSummaryPage then I doesn't get this error.
ds(wait: true)
{ module DatasourceInformationRow, $("table.ic-table-creditReportProduct table tr", it) }
class ApplicationSummaryPage extends Page
{
static url = "application-summary/application-summary.jsf"
static at =
{ assert title == "Application Summary" }
static content =
{
menu
{ module MenuModule }
userPanel
{module UserPanelModule }
ds(wait: true)
{ module DatasourceInformationRow, $("table.ic-table-creditReportProduct table tr", it) }
applicationSummaryForm
{ $("#applicationSummary_form") }
workItemDiv
{$("div", id: "consumerWorkItems:workItemPanel")}
workItemsSection
{workItemDiv.find (".ui-panel-title").text()}
resubmitLink
{$("a",id: "loginForm")}
overrideDecisionLink
{$("a",id: "loginForm")}
addNoteLink
{$("a",id: "loginForm")}
}
}
Spec
class SearchSpec extends BaseUiSpec
{
def setup()
{
login("manager")
}
def "cccc"()
{
when: "I search"
to SearchPage
at SearchPage
applicationid.value "10002000000010000"
searchButton.click()
at SearchPage
waitFor
{ searchResultsData }
println "------------"+ searchResults(0).ApplicationId.text()
searchResults(0).ApplicationId.click(ApplicationSummaryPage)
Thread.sleep 5000
at ApplicationSummaryPage
println "-----???"+ ds
}
}
Error
geb.waiting.WaitTimeoutException: condition did not pass in 15.0 seconds (failed with exception)
at geb.waiting.Wait.waitFor(Wait.groovy:126)
at geb.content.PageContentTemplate.create(PageContentTemplate.groovy:117)
at geb.content.PageContentTemplate.get(PageContentTemplate.groovy:98)
at geb.content.NavigableSupport.getContent(NavigableSupport.groovy:43)
at geb.content.NavigableSupport.propertyMissing(NavigableSupport.groovy:127)
at geb.Browser.propertyMissing(Browser.groovy:175)
at geb.spock.GebSpec.propertyMissing(GebSpec.groovy:55)
at test.SearchSpec.cccc(SearchSpec.groovy:33)
Caused by: geb.error.InvalidPageContent: Definition of page component template '$' of 'ApplicationSummaryPage' is invalid, params must be either a Closure, or Map and Closure (args were: [class java.lang.String, null])
at geb.content.PageContentTemplateBuilder.throwBadInvocationError(PageContentTemplateBuilder.groovy:69)
at geb.content.PageContentTemplateBuilder.methodMissing(PageContentTemplateBuilder.groovy:51)
at groovy.lang.GroovyObjectSupport.invokeMethod(GroovyObjectSupport.java:44)
at com.equifax.ic.testing.framework.ui.pages.applicationmanagement.ApplicationSummaryPage._clinit__closure2_closure5(ApplicationSummaryPage.groovy:24)
at com.equifax.ic.testing.framework.ui.pages.applicationmanagement.ApplicationSummaryPage._clinit__closure2_closure5(ApplicationSummaryPage.groovy)
at geb.content.PageContentTemplate.invokeFactory(PageContentTemplate.groovy:134)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy:103)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy)
at geb.waiting.Wait.waitFor(Wait.groovy:115)
... 7 more
The problem was that i wan't passing the index for ds. The corrected version is below
println "-----???"+ ds(0)
Got the response on Geb mailing list. Posting here for others.
I am reading through a table in jquery and I am trying to pass the value to a method from a class. I am trying to write the following but can`t figure what is wrong.
The error message is on the line if...:
MvcUI\Views\Shared\employee.cshtml(170,81): error CS1646: Keyword, identifier, or string expected after verbatim specifier: #
MvcUI\Views\Shared\employee.cshtml(170,83): error CS1056: Unexpected character '$'
MvcUI\Views\Shared\employee.cshtml(170,90): error CS1056: Unexpected character '$'
MvcUI\Views\Shared\employee.cshtml(170,103): error CS1012: Too many characters in character literal
Help please.
if($.trim($(this).find('td:first').text())!='')
{
#{
if (MvcUI.Employee.ContainsName(#:$.trim($(this).find('td:first').text())))
{
#:$($(this).find('td:first').html($(this).find('td:first').text() + "<br />Name");
}
}
}
There 2 way to do what you want:
Preload employer names into some js array
Use ajax to check if name is registred
I will describe the second way.
The JS code:
var tdText = jQuery.trim($(this).find('td:first').text());
if(tdText != '') {
jQuery.post
(
'/some_route/employers/checkname',
{
name = tdText
},
function(rsp) {
if(rsp == 'true') {
alert("name is registered"); //replace by your code
}
else
{
alert("name isn't registered"); //replace by your code
}
}
);
}
The C# controller code:
[HttpPost]
public ActionResult CheckName(string name)
{
return Json(MvcUI.Employee.ContainsName(name));
}