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.
Related
I was going through the relay docs and came to following code in RANGE_ADD.
class IntroduceShipMutation extends Relay.Mutation {
// This mutation declares a dependency on the faction
// into which this ship is to be introduced.
static fragments = {
faction: () => Relay.QL`fragment on Faction { id }`,
};
// Introducing a ship will add it to a faction's fleet, so we
// specify the faction's ships connection as part of the fat query.
getFatQuery() {
return Relay.QL`
fragment on IntroduceShipPayload {
faction { ships },
newShipEdge,
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'faction',
parentID: this.props.faction.id,
connectionName: 'ships',
edgeName: 'newShipEdge',
rangeBehaviors: {
// When the ships connection is not under the influence
// of any call, append the ship to the end of the connection
'': 'append',
// Prepend the ship, wherever the connection is sorted by age
'orderby(newest)': 'prepend',
},
}];
}
/* ... */
}
Now over here it is mentioned that edgeName is required for adding new node to the connection. Looks well and fine.
Now, I move further down the documentation and reached the GraphQL implementation of this mutation.
mutation AddBWingQuery($input: IntroduceShipInput!) {
introduceShip(input: $input) {
ship {
id
name
}
faction {
name
}
clientMutationId
}
}
Now according to docs this mutation gives me output as
{
"introduceShip": {
"ship": {
"id": "U2hpcDo5",
"name": "B-Wing"
},
"faction": {
"name": "Alliance to Restore the Republic"
},
"clientMutationId": "abcde"
}
}
I cannot see edgeName being present here.
I was using graphene for my project. Over there also I saw something similar only
class IntroduceShip(relay.ClientIDMutation):
class Input:
ship_name = graphene.String(required=True)
faction_id = graphene.String(required=True)
ship = graphene.Field(Ship)
faction = graphene.Field(Faction)
#classmethod
def mutate_and_get_payload(cls, input, context, info):
ship_name = input.get('ship_name')
faction_id = input.get('faction_id')
ship = create_ship(ship_name, faction_id)
faction = get_faction(faction_id)
return IntroduceShip(ship=ship, faction=faction)
Over here also I cannot see edgeName anywhere.
Any help please? I am working on mutations for the first so wanted to confirm a m I missing something or is something wrong here?
This example might be either simplified or a bit obsoloete, because in practice there is need to return edge and that's exactly what is fetched by relay (other fields in RANGE_ADD are more a kind of declaration and are not necessarily fetched).
Here is how you can do it in graphene:
# Import valid as of graphene==0.10.2 and graphql-relay=0.4.4
from graphql_relay.connection.arrayconnection import offset_to_cursor
class IntroduceShip(relay.ClientIDMutation):
class Input:
ship_name = graphene.String(required=True)
faction_id = graphene.String(required=True)
ship = graphene.Field(Ship)
faction = graphene.Field(Faction)
new_ship_edge = graphene.Field(Ship.get_edge_type().for_node(Ship))
#classmethod
def mutate_and_get_payload(cls, input, context, info):
ship_name = input.get('ship_name')
faction_id = input.get('faction_id')
ship = create_ship(ship_name, faction_id)
faction = get_faction(faction_id)
ship_edge_type = Ship.get_edge_type().for_node(Ship)
new_ship_edge = edge_type(
# Assuming get_ships_number supplied
cursor=offset_to_cursor(get_ships_number())
node=ship
)
return cls(ship=ship, faction=faction, new_ship_edge=new_ship_edge)
Please Refer to my plunkr
I've been playing around with the new Angular 2 RC and I think I have figured out how the form validation works.
First I build 2 objects called defaultValidationMessages and formDefinition
private defaultValidationMessages: { [id: string]: string };
formDefinition: {
[fieldname: string]:
{
displayName: string,
placeholder: string,
currentErrorMessage: string,
customValidationMessages: { [errorKey: string]: string }
defaultValidators: ValidatorFn,
defaultValue: any
}
};
Then I load up those objects with the default validators and field information. and build the ControlGroup from the formDefinition object.
this.defaultValidationMessages = {
'required': '{displayName} is required',
'minlength': '{displayName} must be at least {minlength} characters',
'maxlength': '{displayName} cannot exceed {maxlength} characters',
'pattern': '{displayName} is not valid'
}
this.formDefinition = {
'name': {
displayName: 'Name',
placeholder: '',
currentErrorMessage: '',
customValidationMessages: {},
defaultValidators: Validators.compose(
[
Validators.required,
Validators.minLength(3),
Validators.maxLength(50)
]),
defaultValue: this.person.name
},
'isEmployee': {
displayName: 'Is Employee',
placeholder: '',
currentErrorMessage: '',
customValidationMessages: {},
defaultValidators: Validators.compose([]),
defaultValue: this.person.isEmployee
},
'employeeId': {
displayName: 'Employee Id',
placeholder: '',
currentErrorMessage: '',
customValidationMessages: { 'pattern': '{displayName} must be 5 numerical digits' },
defaultValidators: Validators.compose(
[
Validators.pattern((/\d{5}/).source)
]),
defaultValue: this.person.employeeId
}
}
this.personForm = this.formBuilder.group({});
for (var v in this.formDefinition) {
this.personForm.addControl(v, new Control(this.formDefinition[v].defaultValue, this.formDefinition[v].defaultValidators));
}
this.personForm.valueChanges
.map(value => {
return value;
})
.subscribe(data => this.onValueChanged(data));
Using a technique that I learned from Deborah Kurata's ng-conf 2016 session I bind a method to the ControlGroups valueChanges event.
By defining sets of default validators on each control it allows the control to dynamically append new validators to it based on future action. And then clearing back to the default validators later.
Issue I still have.
I was having an issue getting my typescript intellisense to import the ValidatorFn type. I found it here but I don't think I'm suppose to access it like this:
import { ValidatorFn } from '../../../node_modules/#angular/common/src/forms/directives/validators'
I also had to reset the form by setting some internal members. Is there a better way to reset the form? see below:
(<any> this.programForm.controls[v])._touched = false;
(<any> this.programForm.controls[v])._dirty = false;
(<any> this.programForm.controls[v])._pristine = true;
Please look at my plunk and let me know if there is a better way to handle model driven dynamic form validation?
My import string looks like this and it isn't marked as an error.
import { ValidatorFn } from '#angular/common/src/forms/directives/validators';
And some info about the reset form issue. There is not proper reset feature available yet, but a workaround exists. I've found it in docs.
You need a component field
active: true;
and you need to check it in your form tag:
<form *ngIf="active">
After that you should change your personFormSubmit() method to:
personFormSubmit() {
this.person = new Person();
this.active = false;
setTimeout(()=> {
this.active=true;
this.changeDetectorRef.detectChanges();
alert("Form submitted and reset.");
}, 0);
}
I tried this solution with you plnkr example and seems that it works.
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.
First off: As far as I can tell, this is not a duplicate. The question “Grails: Custom validator based on a previous value of the field” got an answer suggesting the method getPersistentValue().
I am using Grails 2.2.3. The context is a “current password” field, where a user enters his current password before being allowed to change it to a new one.
So I have tried using getPersistentValue(), by means of something like this as a custom validator:
previousPassword(validator: { val, obj ->
def previous = obj.getPersistentValue('password')
if(val != previous) {
return ['yoErrorCode']
}
})
When I now try to update the user’s password in a controller action:
def changePassword() {
User user = User.get(springSecurityService?.currentUser?.id)
user.validate(["previousPassword"])
user.password = springSecurityService.encodePassword(params.updatedPassword)
if (user.errors.allErrors.size() == 0) {
try {
user.save(failOnError: true)
}
catch(Exception e) {
flash.message = "An error occurred"
render(view: "password_change", model: [user: user])
return
}
flash.message = "Updated password"
render(view: "password_change", model: [user: user])
return
} else {
render(view: "password_change", model: [user: user])
return
}
}
I get an error:
java.lang.IllegalArgumentException: object is not an instance of declaring class
Am I doing this wrong (the correct answer is probably “Yes”)? Or is there some other way of doing this?
I'm just trying to validate an email address in a Controller which I thought would be simple. The method I've done is as follows:
def emailValidCheck(String emailAddress) {
EmailValidator emailValidator = EmailValidator.getInstance()
if (!emailAddress.isAllWhitespace() || emailAddress!=null) {
String[] email = emailAddress.replaceAll("//s+","").split(",")
email.each {
if (emailValidator.isValid(it)) {
return true
}else {return false}
}
}
}
This is being used with a sendMail function, which my code for that is here:
def emailTheAttendees(String email) {
def user = lookupPerson()
if (!email.isEmpty()) {
def splitEmails = email.replaceAll("//s+","").split(",")
splitEmails.each {
def String currentEmail = it
sendMail {
to currentEmail
System.out.println("what's in to address:"+ currentEmail)
subject "Your Friend ${user.username} has invited you as a task attendee"
html g.render(template:"/emails/Attendees")
}
}
}
}
This works and sends emails to valid email addresses, but if I put in something random that is not an address just breaks with sendMail exception. I can't understand why it's not validating correctly and even going into the emailTheAttendees() method ... which is being called in the save method.
I would suggest using constraints and a command object to achieve this. Example:
Command Object:
#grails.validation.Validateable
class YourCommand {
String email
String otherStuffYouWantToValidate
static constraints = {
email(blank: false, email: true)
...
}
}
Call it like this in your controller:
class YourController {
def yourAction(YourCommand command) {
if (command.hasErrors()) {
// handle errors
return
}
// work with the command object data
}
}