:D
I was following a tutorial in a book, and I did follow it thoroughly.
However, come the part where I am supposed to write an integration test, it suddenly failed saying: Cannot invoke method addToPosts() on null object right after I ran the test. I wonder, what could be wrong... :| Please help! :) Below is the code for the test:
void testFirstPost() {
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save()
def post = new Post (content: 'hellloo oasdo sjdosa daodkao ')
user.addToPosts(post)
assertEquals 1, User.get(user.id).posts.size()
}
Here is the user class:
class User {
String userID
String password
String homepage
Profile profile
static hasMany=[posts:Post, tags:Tag]
static constraints = {
userID (unique: true, size: 6..20)
password (size: 6..20, validator:{password,userID-> return password !=userID.userID}) //validator = The password must not match the username.
homepage (url:true, nullable: true)
profile (nullable: true)
}
}
Here is the Post class:
class Post {
String content
Date dateCreated
static constraints = {
content (blank:false)
}
static belongsTo = [user:User]
static hasMany = [tags:Tag]
static mapping = {
sort dateCreated: "desc"
}
}
save() returns null if validation fails, and "www.geeee.com" isn't a valid URL. It would work with "http://www.geeee.com".
But you should split the creation and saving into 2 steps so you can check it:
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com')
user.save()
assertFalse user.hasErrors()
or use failOnError if you are confident that that part should succeed and only want to test the other parts, e.g.
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save(failOnError: true)
Related
I'm trying to create (my first ever) taglib in grails and I sort of got it down except that I'm trying to return a list/array of strings that has to be displayed in a table but all I'm getting is the first value of the list. Here's what I got:
Taglib:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
def realRoles = []
roles.each { role ->
realRoles.add(role.authority.substring(5))
}
out << realRoles
out << body()
}
}
Html:
<table class="table table-bordered table-hover" style="background-color: #FFFFFF;">
<thead>
<tr style="background-color: #007FB2;color: #FFFFFF;">
<th>ID</th>
<th>Name</th>
<th>Surname</th>
<th>Email</th>
<th>Roles</th>
<th>Delete</th>
</tr>
</thead>
<g:each in="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.firstName}</td>
<td>${user.surname}</td>
<td>${user.username}</td>
<td><g:roles user="${user.id}" /></td>
<td><button type="button" class="gen-btn btn-red" style="width: 100%;" onclick="confDelete(${user.id}, '${user.firstName}');">Delete</button></td>
</tr>
</g:each>
</table>
What's supposed to happen is when the taglib gets called it's supposed to go get all the roles associated with the user id then take each role's authority property and cut the preceding "ROLE_" off of it. So ROLE_ADMIN just becomes ADMIN and ROLE_USER just becomes USER, etc. What I'd ideally like is a list that I can loop through in the gsp but I realise that's alot to ask so if I can just get help getting a comma separated list roles back from my taglib I'd really appreciated it.
To make it more clear, what I'm currently getting back is literally [ROLE_ADMIN] regardless of whether the user has more roles than that. What I want is a complete list of roles, e.g. ROLE_ADMIN, ROLE_MANAGER, ROLE_WHATEVER.
Thanks in advance
EDIT Here are my User, Role and UserRole domain classes. I am using Spring security to generate these classes.
User:
package fake.package.name
class User {
transient springSecurityService
String username
String password
String firstName
String surname
Date dateCreated
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static constraints = {
username blank: false, unique: true
password blank: false
firstName blank: false
surname blank: false
}
static mapping = {
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
String toString(){
if((firstName)&&(surname))
firstName+ " " +surname
else
username
}
}
Role:
package fake.package.name
class Role {
String authority
static mapping = {
cache true
}
static constraints = {
authority blank: false, unique: true
}
}
UserRole:
package fake.package.name
import org.apache.commons.lang.builder.HashCodeBuilder
class UserRole implements Serializable {
User user
Role role
boolean equals(other) {
if (!(other instanceof UserRole)) {
return false
}
other.user?.id == user?.id &&
other.role?.id == role?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (role) builder.append(role.id)
builder.toHashCode()
}
static UserRole get(long userId, long roleId) {
find 'from UserRole where user.id=:userId and role.id=:roleId',
[userId: userId, roleId: roleId]
}
static UserRole create(User user, Role role, boolean flush = false) {
new UserRole(user: user, role: role).save(flush: flush, insert: true)
}
static boolean remove(User user, Role role, boolean flush = false) {
UserRole instance = UserRole.findByUserAndRole(user, role)
if (!instance) {
return false
}
instance.delete(flush: flush)
true
}
static void removeAll(User user) {
executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
}
static void removeAll(Role role) {
executeUpdate 'DELETE FROM UserRole WHERE role=:role', [role: role]
}
static mapping = {
id composite: ['role', 'user']
version false
}
}
Do note that because this a work project and not a personal one I'm not allowed to edit the code in here. Also, I changed the package names to fake.package.name, that's not the actual names of the packages.
I don't know, where your User<->Role Association comes from (Spring Security Framework?) or if it's coded by yourself. And you gave no examples of your domain-classes.
Let me explain another solution a bit:
If you design your domain-classes somehow like the following:
package xyz
class User {
String name
static hasMany = [authorities: Role]
static constraints = {
}
}
---------------
package xyz
class Role {
static belongsTo = User
String name
static hasMany = [user: User]
static constraints = {
}
}
a third table 'user_roles' will be created for saving the many-to-many relation/association
With this configuration, you could shorten your taglib to:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def realRoles = []
user.authorities.each { r ->
realRoles.add(r.authority.substring(5))
}
out << realRoles.join(', ')
out << body()
}
}
Thanks to susi I managed to figure this thing out. Here: def roles = Role.findAllById(UserRole.findByUser(user).roleId) I only returned the first UserRole so I couldn't possibly bring back more than one Role. This is what my taglib method looks like now. I realize it might not be the most efficient and if someone would like to improve on it feel free.
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def userRoles = UserRole.findAllByUser(user)
def roles = []
def realRoles = []
userRoles.each { ur ->
roles.add(Role.findById(ur.roleId))
}
roles.each { r ->
realRoles.add(r.authority.substring(5))
}
realRoles = realRoles.join(", ")
println(realRoles)
out << realRoles
out << body()
}
}
Starting after the line if(user) {:
I find all the userRoles associated with the user Id
Then I loop through the list of userRoles and add each ur to a roles list
Then I loop through the list of Roles and add the formatted role's authority to the realRoles list
Lastly I use realRoles = realRoles.join(", ") to remove the square brackets around eachrealRoles` item
the root cause is the line
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
because the UserRole.findByUser(user) just returnes 1 UserRole, roles is just 1 Role object inside a List, instead of all the User's Role objects.
change it to
def roles = Role.findAllByIdInList(UserRole.findAllByUser(user)*.roleId)
as you seem to be relatively new to Grails, a quick explanation: you can use dynamic finders (find*) in 2 ways: findBy* and findAllBy*. as the naming of the methods suggest, findBy* returns 1 object or null. findAllBy* returns a collection of objects.
Additionally, you can combine the property-names with operations (in our case "InList") to specify the type of search operation.
In this example we first retrieve a list of UserRole objects, transform this list into a list of role ids (using the spread operator "*.") and with these ids retrieve all Role objects having one of these IDs.
as the UserRole class contains a Role property directly, of course we can directly write
def roles = UserRole.findAllByUser(user)*.role
keep in mind, that this list could possibly contain null if the property role on UserRole class was nullable (it's not the case here, but if any reader has a similar problem but with a nullable association).
summarized, what you are doing in the taglib at the moment, could be written as:
out << UserRole.findAllByUser(user)*.role.collect({ it.substring(5) }).join(', ')
and just a little comment to the previous answer from susi:
that code shows a direct many-to-many association between User and Role.
as Burt Beckwith wrote some time ago https://mrpaulwoods.wordpress.com/2011/02/07/implementing-burt-beckwiths-gorm-performance-no-collections/
this can have impact on performance as every new User that gets added to a Role requires hibernate to load all existing User-Role-Connections, which could cause trouble when you have lots of users.
That's why the scaffolded domain classes of spring-security-core (since 2.x version iirc) now explicitely define the UserRole-class.
Good day. I am new to grails and I have to design a music-shopping type app. I have a Cart domain and a User domain and what I'm trying to do is when I create a new User object I want to have a new Cart object created too that links to said User object in the User. I'm using static scaffolding so I want to do this in the User/save action. Here's my code:
class Cart {
String item
Integer quantity
BigDecimal price
String type
Integer typeId
User user
static constraints = {
type nullable:false, blank:false, inList:["Album", "Song", "Empty"]
typeId nullable:false, blank:false
}
}
class User {
String username
String password
String fName
String lName
String email
static constraints = {
username(nullable : false, blank : false, minSize : 1)
password(nullable : false, blank : false, minSize : 1)
fName(nullable : false, blank : false, minSize : 1)
lName(nullable : false, blank : false, minSize : 1)
email(nullable : false, blank : false, email : true)
}
}
class UserController {
//static scaffolded code (index, show, create, etc.)
#Transactional
def save(User userInstance){ //This whole method is also generated with scaffolding
if (userInstance == null) {
notFound()
return
}
if (userInstance.hasErrors()) {
respond userInstance.errors, view: 'create'
return
}
userInstance.save flush: true
//This is what I've been trying to do but it doesn't work and I don't know why :(
def myCart = new Cart(user:userInstance, item:'empty item', quantity:0, price:0, total:0, type:'Empty', typeId:0).save(flush:true)
//Rest of the generated code
}
//More generated code
}
Any help would be appreciated. Thanks!
You have 2 bugs in Cart class.
First: update typeID to typeId
Second: type should be String, not BigDecimal
I think this link explains well on how to do cascading in Grails.
Cascading determines what type of actions, when applied to a domain instance, also apply to the relations of that instance.
According to how the cascading goes, I think it makes more sense that you have a Cart object in User class and use belongsTo to let Grails know the Cart is saved or deleted with the User.
I am trying to edit currently logged-in user profile. I have used Spring Security Service plugin for user management. User ( which is subscriber in my application) contains field which come from different domain like:
1. User(subscriber in app): has username, password.
2 Profile:has emailaddress and phonenumber etc.
3. Person:has Firstname and lastname.
All above domains make a complete profile for user(subscriber).
Now I want to edit currently logged-in user profile like firstname, lastname or email.
I tried with following code.
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
if (!person){
flash.message = "could not find user with ${params.id}"
redirect action: list
}
else
[person: person, authorityList: sortedRoles()]
}
else {
redirect(controller: "login" , action:"login");
}
}
But it did not work. Here I got currently logged in user id but profile is null.
Profile domain:
package com.vproc.member
import java.util.Date;
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Team;
class Profile {
String emailAddress // field governed by privacy policy
String phoneNumber // field governed by privacy policy
Date dateCreated
Date lastUpdated
boolean isDefaultProfile
static belongsTo = [ Person]
//ProfilePrivacyLevelEnum privacyLevel = ProfilePrivacyLevelEnum.Private
static constraints = {
}
}
Person domain:
package com.vproc.member
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Notification;
import com.vproc.enquiry.Team;
class Person extends Party{
String firstName
String lastName
Profile profile
static belongsTo = [Organization]
static constraints = {
lastName nullable:true
firstName blank:false
}
}
Subscriber domain:
package com.vproc.member
import java.util.Date;
import com.vproc.common.StatusEnum;
import com.vproc.enquiry.Discussion;
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Notification;
import com.vproc.enquiry.SharedEnquiry;
import com.vproc.enquiry.Team;
import com.vproc.order.Seat;
class Subscriber extends PartyRole{
transient springSecurityService
String username
String password
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
StatusEnum status
Date dateCreated
Date lastUpdated
List<Contact> contacts ;
static belongsTo = [ customer: Customer]
static hasMany = [seats: Seat, ownedEnquiries: Enquiry,enquiresSharedWith: SharedEnquiry,]
static constraints = {
// username validator : { val , obj ->
// if (obj.status != StatusEnum.Pending)
// val!= null
// }
username unique: true
password validator : { val , obj ->
if (obj.status != StatusEnum.Pending)
val != null
}
contacts nullable: true
notifications nullable : true
username nullable: true
password nullable: true
}
}
UserController.groovy
package com.vproc.member
import com.vproc.common.StatusEnum
import com.vproc.exception.CustomValidationException;
class UserController extends AbstractS2UiController {
def saltSource
def userCache
def springSecurityService
def mailService
def messageSource
def create = {
//Subscriber user = lookupUserClass().newInstance(params)
UserCommand command = new UserCommand()
[command: command, authorityList: sortedRoles()]
}
def save = { UserCommand command ->
if (command.hasErrors()) {
render view: 'create', model: [command: command]
return
}
Subscriber user = lookupUserClass().newInstance(params)
Profile profile = new Profile(emailAddress : command.emailAddress, phoneNumber: "234555", isDefaultProfile: "true").save()
Party person = new Person(firstName: command.firstName, lastName: command.lastName, profile: profile).save()
user.party = person
if(! user.party.hasErrors()){
if (params.password) {
String salt = saltSource instanceof NullSaltSource ? null : params.username
user.password = springSecurityUiService.encodePassword(params.password, salt)
user.status = StatusEnum.Active
}else{
user.status = StatusEnum.Pending
}
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
user.customer = loggedinSubscriber.customer
if (!user.save(flush: true)) {
flash.message = "not able to save user"
}
}
else{
flash.message = "not able to save user"
}
//addRoles(user)
//flash.message = "User has been added"
flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])}"
redirect( action : "list" )
}
def edit = {
String username
def user = params.username ? lookupUserClass().findWhere((usernameFieldName): params.username) : null
if (!user) user = findById()
if (!user) return
return buildUserModel(user)
}
// def contacts = Contact.findAllBySubscriber( loggedinSubscriber)
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
if (!person){
flash.message = "could not find user with ${params.id}"
redirect action: list
}
else
[person: person, authorityList: sortedRoles()]
}
else {
redirect(controller: "login" , action:"login");
}
}
}
Now I want to edit profile of currently logged-in user using method userSettings in usercontroller. I got id of currently logged in user id but I am not able to use that id with profile and person.
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
Using above code, profile value is null.
Ok I dont't want to understand all of your domain modeling, so I'm answering on the thing that I noticed right after having a look at your code. I hope it helps, otherwise just use debug and logging a lot to tackle down your error. And read the documentation.
First of all, in Profile.groovy (and other Domains) use a map to define belongsTo:
static belongsTo = [person:Person]
that also does not look right:
Profile profile = Profile?.get(params.id);
When you have a static access to a grovvy class (upper case first letter) you don't need the questionmark. Is params.id supposed to be the ID of the profile? Then you need to use the findBy methods:
Profile profile = Profile.findById(params.id);
Then in the next line you have to use the key from your belongsTo map again, so person instead of Person:
// no get or findBy required
Party person = profile?.person
Hope it helps
I got solution with following code:
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
Party person = Person?.get(loggedinSubscriber.party.id)
Profile profile = person?.profile
[userInstance: profile, authorityList: sortedRoles()]
}
Thanks BurtBeckwith and moeTi.
I am currently working on a grails applications and I have a list of addresses that are attached to an account. Basically what I want to do is when the Account is edited is displays a current list of all the attached Addresses and then I can delete/add as many as I want from the view. When this data is captured it is picked up by the controller and what I want to do is to be able to clear all of the current Addresses from this account and then create the list again with what exists on the view, my code is below:
Account Domain:
class Account {
String name
Date dateCreated
Date lastUpdated
static hasMany = [addresses:Addresses]
static mapping = {
addresses cascade:"all-delete-orphan"
}
def getAddressesList() {
return LazyList.decorate(
addresses,
FactoryUtils.instantiateFactory(Addresses.class))
}
static constraints = {
name(blank:false, unique: true)
}
}
Address Domain:
class Addresses {
int indexVal
String firstLine
String postcode
String area
static belongsTo = [account:Account]
static mapping = {
}
static transients = [ 'deleted' ]
static constraints = {
indexVal(blank:false, min:0)
}
}
Account Controller:
def update() {
def accountInstance = Account.get(params.id)
if (!accountInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'account.label', default: 'Account'), params.id])
redirect(action: "list")
return
}
if (params.version) {
def version = params.version.toLong()
if (accountInstance.version > version) {
accountInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'subscriptions.label', default: 'Subscriptions')] as Object[],
"Another user has updated this Account while you were editing")
render(view: "edit", model: [accountInstance: accountInstance])
return
}
}
accountInstance.properties = params
accountInstance.addresses.clear()
accountInstance.save(flush: true)
....
}
Error:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses. Stacktrace follows:
Message: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses
This error seems to be occurring in the controller on line:
accountInstance.save(flush: true)
I have tried several different way to get this to work and would really appreciate some help.
So it seems like you have done some work that Grails can do for you.
class Account {
String name
Date dateCreated
Date lastUpdated
List addresses
static hasMany = [addresses:Address]
static mapping = {
addresses cascade:"all-delete-orphan"
}
static constraints = {
name(blank:false, unique: true)
}
}
class Address {
String firstLine
String postcode
String area
static belongsTo = [account:Account]
}
This will produce the effect you want of having addresses being a list.
I've found either
instance.addresses = null
or
instance.addresses.clear()
to work for me
When you define addresses cascade:"all-delete-orphan" in Account class you don't need static belongsTo = [account:Account] in Addresses. So just try to remove that statement and test your code. See related link.
I am trying to run the sample Collab-Todo application as in book Beginning Groovy and Grails,page no.123 (Author:Christopher M. Judd,Joseph Faisal Nusairat,and James Shingler Publication:Apress and Edition:2008). Here is my User.groovy file:
package collab.todo
class User {
String userName
String firstName
String lastName
static hasMany = [todos: Todo, categories: Category]
static constraints = {
userName(blank:false,unique:true)
firstName(blank:false)
lastName(blank:false)
}
String toString () {
"$lastName, $firstName"
}
}
The UserController.groovy is as Follows:
package collab.todo
class UserController {
def scaffold = User
def login = {
}
def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'login')
}
session.user = user
redirect(controller:'todo')
}
def logout = {
if(session.user) {
session.user = null
redirect(action:'login')
}
}
}
I am able to create,read,update or delete the User table as usual.Here is a sample screenshot of my User view:
In the scaffolding view, I am trying to show the list of all the users in a drop-down(as per the book) using following snippet inside the user/login.gsp:
<g:select name='userName' from="${User?.list()}"
optionKey="userName" optionValue="userName"></g:select>
But what I am getting in the login page is a dropdown with no values populated:
Here is the screenshot of the login page:
In case I change
from="${User?.list()}"
to
from="${User.list()}"
I am getting a NullPointerException. So any clues what is going on?
It looks like the User class could not be found from your view.
Try one of the following:
Add the import statement to your view.
<%# page import="collab.todo.User" %>
Or use the fully qualified name within from attribute.
from="${collab.todo.User.list()}"
The best practice would be to pass the list of users from the controller:
def login = {
[users: User.list()]
}
And use the collection within your view
from="${users}"