get name of failed constraints - validation

Assume I have a User class that has a username property and there are several constraints defined on this field.
class User {
String username
static constraints = {
username blank: false, unique: true, email: true
}
}
If I call
user.save()
I can then figure out if any of the constraints on the username field failed via
user.errors['username'] != null
But is there a way I can figure out which of the constraints failed?

The value user.errors['username'].codes will contain a number of keys used for looking up validation messages in messages.properties. You can use these to figure out which constraints broke.
For example, user.errors['username'].codes[-1] will contain the constraint part of the messages.properties key:
assert user.errors['username'].codes[-1] == 'blank' // if blank constraint fails
assert user.errors['username'].codes[-1] == 'unique' // if unique constraint fails

Yeap, you can check the error code with code property on the error object:
def user = new User(email: '')
user.validate()
assert user.errors['email'].code == 'blank'
user.email = 'asdasd'
user.validate()
assert user.errors['email'].code == 'email.invalid'
If you may have more than one error in a property, the only way i found to get all the errors for that property is to iterate the allErrors property:
class Foo {
String bar, baz
static constraints = {
bar blank: false
baz email: true, notEqual: 'foobar'
}
}
def foo = new Foo(bar: '', baz: 'foobar')
foo.validate()
foo.errors.allErrors.each {
println "$it.field: $it.code"
}
Should output something like:
bar: blank
baz: notEqual
baz: email.invalid

Related

how to return both error and data in graphql

There are user and notes fields.
If the notes field exceeds the limit, I want to get an error with the data
But I don't know what to do. (Currently only an error is returned.)
Is it possible to return both the error and the data?
as it
errors = {xx}
to be
data = {user: { notes: []}}
errors = {xx}
my code is as follows
app/graphql/types/object_types/user.rb
module Types
module ObjectTypes
class User < Types::ObjectTypes::BaseObject
graphql_name 'User'
implements GraphQL::Types::Relay::Node
field :notes, [Types::ObjectTypes::Note], null: true do
description ''
argument :date_from, GraphQL::Types::ISO8601Date, required: true
argument :date_to, GraphQL::Types::ISO8601Date, required: true
end
def notes(date_from:, date_to:)
object.notes.date_between(date_from, date_to).tap { |array| raise AppError if array.size > 1000 }
end

How do I accept field arguments for a nested query in Graphql Ruby?

I'm getting this error in GraphQl (Apollo JS/ Graphql Ruby):
Error Error: GraphQL error: Field 'pagination' doesn't accept argument 'pagination' GraphQL error: Variable $pagination is declared by Clients but not used. Reload the page and try again.
I have this query:
query Clients($pagination: PaginationInput) {
clients {
data {
....Fragment
}
pagination(pagination: $pagination) {
....Fragment
}
}
}
And I have this as my input type:
class PaginatedClientsType < Types::BaseObject
field :data, ...
field :pagination, PaginationType ... do
argument :pagination, PaginationInput, required: false
end
end
And this in my query_type.rb file:
field :clients, ::Types::Paginated::ClientsType, null: false do
argument :pagination, PaginationInput, required: false
end
def clients(pagination:)
...
end
// and i've added to no avail:
field :pagination ... do
argument :pagination, PaginationInput, required: false
end
def pagination(pagination:)
...
end
Any ideas on how I can pass the argument to something other than this top level client?
I've read docs here but can't figure it out.
Thanks!

Grials update fails on unique constraint

I have a user domain in which I have a unique constraint on it's employee number
class User {
Integer employeeNumber
String employeeLogin
String firstName
String middleName
String lastName
String nickname
Date birthday
static mapping = {
table 'user'
id generator: 'assigned', name: 'employeeNumber', type: 'int'
employeeNumber column: 'employee_number'
version false
sort 'lastName'
}
static constraints = {
employeeNumber(blank: false, nullable: false, unique: true)
employeeLogin(blank: false, nullable: false, unique: true)
firstName(blank: false, nullable: false)
middleName(blank: true, nullable: true)
lastName(blank: false, nullable: false)
nickname(blank: true, nullable: true)
birthday(blank: true, nullable: true)
}
}
and I am trying to update the user with
class UserController {
...
def saveUser() {
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
if (params.userId) { // handles user update for existing employeeNumber
def user = User.findByEmployeeNumber(params.userId) // sent in hidden field name userId with value employeeNumber
user.employeeNumber = Integer.parseInt(params.employeeNumber)
user.employeeLogin = params.employeeLogin
user.firstName = params.firstName
user.middleName = params.middleName
user.lastName = params.lastName
user.nickname = params.nickname
try {
user.birthday = formatter.parse(params.birthday)
}
catch (Exception ignore) {
user.birthday = null
}
if (user.validate()) {
user.save(flush:true, failOnError:true)
redirect(action:'profile', id:user.employeeNumber)
} else {
render(view:'editUser', model:[user:user])
}
} else { // handles new user
... // this part works
}
}
...
}
but it is catching on if (user.validate()) { ... } due to the unique constraints on employeeNumber and employeeLogin.
When creating a new user I want the username and id to be unique, but upon update I'd obviously like to update an existing user, however this unique constraint is blocking me from doing so. Any ideas on how to solve this problem?
You are getting this error because you have made the employeeNumber field as your primary key
For Hibernate/Grails, Primary Key is the identifier value for an object. It is something that will never change during the life of the object
During update you are trying to modify the identifier which is causing the issue.
You can definitely modify a unique field, so in your case you can modify the employeeLogin. But you can't modify the Primary Key.
Also employeeNumber is already a primary key so don't declare it as a unique key. This will remove the unique key error that you are facing.

Play Validation - Custom form field validation with specific field error

case class Address(
address1: String,
city: String,
state: String,
postal: String,
country: String
)
Form(
mapping = mapping(
"address1" -> nonEmptyText,
"city" -> nonEmptyText,
"state" -> nonEmptyText,
"postal" -> nonEmptyText,
"country" -> nonEmptyText
)(Address.apply)(Address.unapply).verifying("Invalid Postal Code!", validatePostal _)
)
def validatePostal(address: Address): Boolean = {
address.country match {
case "US" | "CA" =>
val regex: Regex = ("^(\\d{5}-\\d{4}|\\d{5}|\\d{9})$|^([a-zA-Z]\\d[a-zA-Z]( )?\\d[a-zA-Z]\\d)$").r
regex.pattern.matcher(address.postal).matches()
case _ => false
}
}
Above form validation for postal code works fine with global error displayed on form for invalid US or Canadian postal codes.
I would like to show the error as field error right next to the field than as global error which in my case is shown on top of the form.
Is there a way to use inbuilt Form constraints or verification methods to achieve this instead of FormError's?
You can add the constraint to the field. Then update validatePostal to accept a tuple of the two values instead.
Form(
mapping = mapping(
"address1" -> nonEmptyText,
"city" -> nonEmptyText,
"state" -> nonEmptyText,
"postal" -> tuple(
"code" -> nonEmptyText,
"country" -> nonEmptyText
).verifying("Invalid Postal Code!", validatePostal _),
)((address1, city, state, postal) => Address(address1, city, state, postal._1, postal._2))((address: Address) => Some((address.address1, address.city, address.state, (address.postal, address.country))))
)
Template:
#inputText(
addressForm("postal.code"),
'_label -> "Postal code",
'_help -> "Please enter a valid postal code.",
'_error -> addressForm.error("postal")
)
Defining the error like that you are creating a FormError("","Invalid Postal Code!") object in a form, as it doesn't have key (the first parameter), the framework don't attach the error to the form element.
On form error when you are binding the request to the form, you will have to create a new form removing the FormError("","Invalid Postal Code!") and replacing it with an error FormError("form.id","message")
In our project we created an implicit def for Form to replace the form errors (we couldn't find a way to create dynamic constraint validations) these are the 2 definitions we have:
def replaceError(key: String, newError: FormError): Form[T] = {
val updatedFormErrors = form.errors.flatMap { fe =>
if (fe.key == key) {
if (form.error(newError.key).isDefined) None
else {
if (newError.args.isEmpty ) Some(FormError(newError.key,newError.message,fe.args))
else Some(newError)
}
} else {
Some(fe)
}
}
form.copy(errors = updatedFormErrors.foldLeft(Seq[FormError]()) { (z, fe) =>
if (z.groupBy(_.key).contains(fe.key)) z else z :+ fe
})
}
def replaceError(key: String, message: String, newError: FormError): Form[T] = {
def matchingError(e: FormError) = e.key == key && e.message == message
val oldError = form.errors.find(matchingError)
if (oldError.isDefined) {
val error = if (newError.args.isEmpty) FormError(newError.key,newError.message,oldError.get.args) else newError
form.copy(errors = form.errors.filterNot(e => e.key == key && e.message == message)).withError(error)
}
else form
}
We have those in a class called FormCryptBind (because we also improve the form object with some crypto stuff) and we define the implicit def like this:
implicit def formBinding[T](form: Form[T])(implicit request: Request[_]) = new FormCryptBind[T](form)
We do it like that because just importing the object having this implicit definition, you can use all the FormCryptBind definitions as they were Form's
And we use it like this
import whatever.FormImprovements._
...
object SomeController extends Controller{
...
def submit = Action{ implicit request =>
form.bindRequest.fold(
formWithErrors => {
val newForm = formWithErrors.replaceError("", "formField.required", FormError("formField", "error.required")
BadRequest(someView(newForm)
},
formDetails => Redirect(anotherView(formDetails))
}
As I can't put actual live code from the app, I touched it a little bit :D so expect compile errors if you copy & paste

grails domain object unexpectedly saved during validation

Considering the following domain classes :
class EnrichmentConfig {
String name
String description
String concept
List fields = []
static hasMany = [fields: FieldConfig]
static constraints = {
name(maxSize: 60, blank: false, nullable: false, unique: true)
concept(maxSize: 255, blank: false, nullable: false)
description(nullable: true, blank: true)
fields(nullable: false, validator: { fields, enrichmentConfig ->
if (fields?.isEmpty()) {
return ['empty']
} else {
return true
}
})
}
static mapping = {
description(type: 'text')
fields(cascade: "all-delete-orphan")
sort('name')
}
}
and
class FieldConfig {
List providers = []
static hasMany = [providers: String]
static belongsTo = [mainConfig: EnrichmentConfig]
static constraints = {
providers(nullable: false, validator: { providers, fieldConfig ->
// some custom validation
})
}
static mapping = {
providers(cascade: 'all-delete-orphan', lazy: false)
}
}
Here the code I use to update an EnrichmentConfig instance in the associated controller:
def update = {
def enrichmentConfig = EnrichmentConfig.get(params.long('id'))
if (enrichmentConfig) {
enrichmentConfig.properties = params
if (enrichmentConfig.validate()) {
if (enrichmentConfig.save(flush: true, failOnError: true)) {
flash.message = "${message(code: 'enrichmentConfig.updated.message', args: [enrichmentConfig.name])}"
redirect(controller: 'enrichment')
}
} else {
// re-validation to attach an error object to each eroneous fieldConfig
enrichmentConfig.fields?.each { it.validate() }
}
render(view: 'fields', model: getFieldsModel(enrichmentConfig))
return
} else {
flash.message = "${message(code: 'enrichmentConfig.not.found.message', args: [params.id])}"
redirect(controller: 'enrichment')
}
}
I've noticed that when I validate an instance of EnrichmentConfig to be updated, associated FieldConfig instances are unexpectedly saved in the database even though they are invalid.
In fact, in debug ste-by-step mode, while enrichmentConfig.validate() is executed, the following appears in the console:
Hibernate:
update
field_config_providers
set
providers_string=?
where
field_config_id=?
and providers_idx=?
How can this be happening? What am I doing wrong?
I should specify that I use grails 1.3.7.
Thanks in advance for your help.
This is just a guess but possibly someplace to start. I don't pretend to understand when Hibernate decides to flush sessions and partially save data and the like. But what I do know is that putting all write related calls in a service saves me a ton of grief over time.
Try moving some of your update method to a service and see if you have better luck. My hunch is that possibly, hibernate needs to persist some of the data to do other stuff and if it were in a transactional service, that write would rollback once the RuntimeException is thrown.
I suggest using a service to save your objects. First, check all the objects for validity using the validate() method.
Then, save the objects in the order in which they depend or in the hierarchy they follow.

Resources