Grails custom validation scenarios - forms

I just started looking at grails, I used to work mainly with PHP before, so my question is what is the best way of adding custom validation scenarios to a form. So lets say we have all the constraints in the domain but under a certain controller action where a form is bound to the domain we want to add an extra custom validation of some sort (for argument sakes, lets say we have a date and only in this scenario it should be more than 2 weeks in the future). Whats the best approach of doing that?
Thanks!

For you requirement you can using a Command Object with constraints:
#grails.validation.Validateable
class CommandObject {
Date date
static constraints = {
date(/*constraint here*/)
}
}
Edit:
Concerning your second question, yes you can share constraints from your domain object to your command object using importFrom [EntityName], see the example from the doc:
Domain object:
class User {
String firstName
String lastName
String passwordHash
static constraints = {
firstName blank: false, nullable: false
lastName blank: false, nullable: false
passwordHash blank: false, nullable: false
}
}
Command object:
class UserCommand {
String firstName
String lastName
String password
String confirmPassword
static constraints = {
importFrom User
password blank: false, nullable: false
confirmPassword blank: false, nullable: false
}
}
Sharing Constraints

I suggest you do the form validation using jquery before it reaches the controller. If you want, you can also do individual validation on the gsp itself (ei. checking that the input field is required, accepts only emails, accepts integer only etc.). You can even do this on the controller. But I think form validation using jquery is already enough :)

Related

replace ObjectId field with custom string for ObjectIdColumn in TypeORM/MongoDB

I have a NestJs REST API and use TypeORM with MongoDB. I want to create a entity called project. I started with a basic entity and I just read that I should use ObjectIdColumn instead of PrimaryColumn for MongoDB.
#Entity()
export class Project extends BaseEntity {
// The technical project name
#ObjectIdColumn({ generated: false })
public id: ObjectID;
// The display name
#Column({ unique: true })
public name: string;
// The project successor
#Column({ nullable: true })
public successorId: ObjectID;
// Configuration stuff for that project
#Column()
public configuration: object;
}
I would like to know if it's possible to replace that object id column with a primary column of type string. The id field is based on a special pattern, e.g. the name field
my awesome project
would result into
my-awesome-project
for the id field. Sure I made use of generated: false but I have to pass in a custom string instead of an ObjectID. Currently this is not possible because the docs say the ObjectID
Can be a 24 byte hex string, 12 byte binary string or a Number. http://mongodb.github.io/node-mongodb-native/2.1/api/ObjectID.html
So what needs to get done to use a custom string as an ID field? The only thing I can think of is creating a second field e.g. theRealId and treat it like the ID field and ignore the autogenerated ObjectId...
From what I've learnt, here is what you can do
#Entity()
export class UserEntity
{
#ObjectIdColumn()
_id: string;
#PrimaryColumn()
id: string;
// The display name
#Column({ unique: true })
public name: string;
#Column({ nullable: true })
public successorId: ObjectID;
#Column()
public configuration: object;
}
MongoDB will use _id as an internal id, that you do not expose through your program (and api, then)
You will work with the id, "normally', and it will be your primary key, generating automatically and so on
Source : personal learning, and Udemy course : NestJS Zero to Hero - Modern TypeScript Back-end Development

GORM - get raw DB value for domain class properties

I'm using GORM for MongoDB in my Grails 3 web-app to manage read/writes from DB.
I have the following 2 domain classes:
class Company {
String id
}
class Team {
String id
Company company
}
For teams, their company is saved on DB as String, and with GORM I can simply use team.company to get an instance of Company domain class.
However, I need to override the getter for company, and I need the raw value for company id (as stored on DB), without GORM getting in the way and performing its magic.
Is there a way to get the raw String value?
Any help is welcome! Thanks in advance
Update (May 27)
Investigating #TaiwaneseDavidCheng suggestion, I updated my code to
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
company attr: "company" // optional
companyId attr: "company", insertable: false, updateable: false
}
}
Please note that I'm using GORM for MongoDB, which (citing the manual) tries to be as compatible as possible with GORM for Hibernate, but requires a slightly different implementation.
However I found out (by trial&error) that GORM for MongoDB doesn't support a similar solution, as it seems only one property at a time can be mapped to a MongoDB document property.
In particular the last property in alphabetical order wins, e.g. companyId in my example.
I figured out a way to make the whole thing work, I'm posting my own answer below.
given a non-insertable non-updateable column "companyId" in domain class
class Company {
String id
}
class Team {
String id
Company company
Long companyId
static mapping = {
company column:"companyId"
companyId column:"companyId",insertable: false,updateable: false
}
}
(Follows the edit to my question above)
I defined a custom mapping, and made use of Grails transients by also defining custom getter and setter for team's company.
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
companyId attr: "company" // match against MongoDB property
}
static transients = [ 'company' ] // non-persistent property
Company getCompany() {
return Company.get(companyId)
}
void setCompany(Company company) {
companyId = company.id
}
}

Spring boot / mongo wont create index with the index annotation

I have the following:
#Document(collection = "linkmetadata")
public class LinkMetaData {
#Indexed(unique = true)
private String url;
...
}
But whenever it creates the collection it doesn't create any index for the url field, it's like it just ignores the annotation. Any idea why this is?
Edit: no index is created upon insertion of data either. And when I try to fetch data for a specific url it throws an error that the url key is not unique if I entered the same url twice, but it doesn't care about inserting the unique key since there is no index..
use auto-index-creation: true in your application properties.Add billow line in your application.properties
spring.data.mongodb.auto-index-creation: true
I found the problem. I had another collection also with a url field marked as unqiue. I had to specify the name of the index on one of them otherwise it seems that it considered that the index already exists even though it was on two different collections
#Indexed(name = "meta_url_index_unique", unique = true)
private String url;
Edit: This answer was before the author updates his question
I believe you need to use the #Document annotation on top the class declaration
So your class should be
#Document
public class LinkMetaData {
#Indexed(unique = true)
private String url;
...
}
I Also had the same issue. The one that solved my issue was adding the index from the mongo db level
db.city.createIndex( { "name": 1 }, { unique: true } );

Grails with MongoDB not saving null value

This is a customer domain class in Grails(v3.1):
class Customer {
static mapWith = 'mongo'
String id
String email
Boolean blacklisted
String name
String telephone
Date dateCreated
Date lastUpdated
String language = 'en'
static constraints = {
email nullable: false
blacklisted nullable: false
name nullable: true
language nullable: true
telephone nullable: true
}
static mapping = {
version false
}
}
I can insert and update into the Customer collection in MongoDB using this class, and it is working fine. The issue occurs when I try to save one of the fields with null value.
customer.telephone = null
customer.save()
Setting a value to null has no effect in the field in MongoDB collection, its value will remain set to the value before the update. For example, if the telephone is set to "1234567" and when I update it to null, the value in MongoDB remains "1234567".
I have tried using failOnError: true and flush: true in save(), both are not working. Any suggestions?
You could try using the mongo driver directly to see if it is a mongo issue or a Gorm issue.
Customer.collection.updateOne([_id:'the-id'],[telephone:null])

Grails MongoDB Update object with Association

I can't seem to understand where I am going wrong currently when I attempt to update my User domain model that has a hasOne association to a Profile object.
My domain models are as follows:
class User {
static hasOne = [profile: Profile]
static fetchMode = [profile: 'eager']
ObjectId id
String username
}
class Profile {
static belongsTo = [user: User]
ObjectId id
String familyName
String givenName
}
I am able to persist a User with a profile originally but when attempting to update the User object I get validation errors.
Validation error occurred during call to save():
- Field error in object 'co.suitable.User' on field 'profile.familyName': rejected value [null]
- Field error in object 'co.suitable.User' on field 'profile.givenName': rejected value [null]
I am able to print out the user.profile ID and also the user.profile.familyName before saving the object. Like the following:
println(user.profile.familyName)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
But I still get the validation errors before saving, i'd imagine that the println(user.profile.familyName) call is fetching the profile object if it already hasn't been loaded which I thought setting the fetchMode would have handled.
The object is able to successfully persist and save when I do:
user.profile = Profile.findById(user.profile.id)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
I could wrap that in a service but I was hoping for a solution that would be handled by Grails if possible. Any advice or thoughts is much appreciated.
You should not apply the logic for the SQL DB to Mongo 1 to 1. Mongo and other document-oriented DBs are not originally intended to store the joins between collections. There are some workarounds, like db-refs, but they are to be used with caution.
For your case - with hasOne - I would suggest using mongo's subdocuments (mirrored as GORM's embedded objects) instead of referencing:
class User {
ObjectId id
String username
Profile profile
static embedded = [ 'profile' ]
}
class Profile {
String familyName
String givenName
}
thus you use the mongo in accordance to it's original puprose. Also querying is simpler and faster.