Working with grails 2.4.0 and RESTful controllers (JSON) and looking for guidance.
I have very simple domains (from a sample):
class Project {
String name
String description
static belongsTo = [owner : EndUser]
static constraints = {
name(blank: false, unique: true)
description()
}
}
class EndUser {
String userName
String fullName
String toString() {
"${fullName}"
}
static hasMany = [projects : Project]
static constraints = {
fullName()
userName(unique: true)
}
}
In ProjectController, I have kept html too for testing in addition to JSON.
class ProjectController extends RestfulController {
static responseFormats = ['html', 'json', 'xml']
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Project.list(params), model:[projectInstanceCount: Project.count()]
}
def show(Project projectInstance) {
respond projectInstance
}
def listWithFullName(){
def a = Project.list(params)
a.owner.fullName
respond a
}
}
If go to html page: /project/show/1, I get html page with details:
HTML page:
Name: prj1
Description: prj desc
Owner: full name1
So far so good, if I do same with json (/project/show/1.json), I get:
{
class: "testapp.Project"
id: 1
description: "prj desc"
name: "prj1"
owner: {
class: "testapp.EndUser"
id: 2
}
}
As you see for "owner" field in JSON I am getting "id". How can I get fullname for owner in JSON. I thought I can do some thing like what I attempted in controller in method: listWithFullName() but that did not work. Any pointers? Thanks.
Related
We're writing an json API with grails. Here is a simplify example with the domains and controller:
class Employee {
String name
Section section
}
class Section {
static hasMany = [employees: Employee]
String name
}
#Transactional(readOnly = true)
class SectionController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
static responseFormats = ['json']
def index() {
request.withFormat {
json {
respond Section.list()
}
}
}
}
I want to get the Section list without employees, something like:
[
{"class":"Section","id":1,"name":"A"},
{"class":"Section","id":2,"name":"B"},
{"class":"Section","id":3,"name":"C"},
{"class":"Section","id":4,"name":"D"},
{"class":"Section","id":5,"name":"F"}
]
But the controller returns:
[
{"class":"Section","id":1,"name":"A","employees":[{"class":"Employee","id":1},{"class":"Employee","id":2},{"class":"Employee","id":3}]},
{"class":"Section","id":2,"name":"B","employees":[{"class":"Employee","id":4},{"class":"Employee","id":5},{"class":"Employee","id":6},{"class":"Employee","id":7}]},
{"class":"Section","id":3,"name":"C","employees":[{"class":"Employee","id":8},{"class":"Employee","id":9},{"class":"Employee","id":10}]},
{"class":"Section","id":4,"name":"D","employees":[{"class":"Employee","id":11}]}
]
When I delete the hasMany relationship, I get what I want, however, I need the relationship to do complex Criteria and hql queries
How can I do to return data without hasMany relationship.
Thanks.
I'm trying to write a unit test for a simple controller that takes GSON as input and writes to DB.
GSON because, the input has nested objects.
Unit testing works if i use JSON, but JSON has its limitations with nested objects.
Domain Class:
class Artist {
String guid
String name
static constraints = {
name nullable: true
}
static mapping = {
guid blank:false, unique: true
}
static hasMany = [albums: Albums]
/*static marshalling={ deep 'albums' }*/
}
Controller implementation:
def create() {
def artist = new Artist(request.GSON)
if (!artist.save(flush: true)) {
artist.errors.each {
log.error("Error while creating Artist " + it)
}
render status: 500, layout: null
return
}
response.status = 201
render artist as GSON
}
Unit test:
#TestMixin(GsonUnitTestMixin)
#TestFor(ArtistController)
#Mock(Artist)
class ArtistControllerTests {
void testCreate() {
request.GSON = "{guid:123,"+
"name: 'Akon',"+
"albums: ["+
"{guid:1,"+
"name:'album 1'"+
"}]}"
controller.create()
assert response.status == 201
}
}
Exception:
Cannot get property 'manyToOne' on null object at
def artist = new Artist(request.GSON) in the controller
Any help will be greatly appreciated..
I have the following:
"/api/users"(controller: "user") {
action = [GET:"list"]
}
Doing a call to http://localhost:8080/platform/users I get a list of users back. Then I added this:
"/api/users"(controller: "user") {
action = [POST:"save"]
}
And now I get a 404 and it is not hitting either method in UserController. I'd like to be able to use the same URL with the verb controlling which action. Am I doing this wrong or does Grails not support this?
From the Grails docs: URL Mappings
static mappings = {
"/product/$id"(controller:"product") {
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
}
For your case:
"/api/users"(controller: "user") {
action = [GET:"list",POST:"save"]
}
Check your userController to see if there is allowedMethods defined accordingly like this:
class UserController {
static allowedMethods = [save: "POST", list: "GET"]
def list() {
.....
}
def save() {
.....
}
}
I have a Blog domain class, which has many messages:
class Blog {
String description
static hasMany = [messages : Message]
static belongsTo = [owner : User]
static constraints = {
description blank: true, nullable: true
}
}
class Message {
String content
String title
User author
Date dateCreated
Date lastUpdated
static hasMany = [comments : Comment]
static constraints = {
content blank: false
author nullable: false
title nullable: false, blank: false
}
static mapping = {
content type: "text"
sort dateCreated: 'desc'
}
}
Message is used also in other places of the application, so association is unidirectional. How can I get 20 latest blog messages, ordered by creation date? By latest blog messages, I mean 20 latest messages which are associated with ANY blog.
class Blog {
...
...
static hasMany [messages: BlogMessages]
...
...
}
class Message {
...
// exactly like you have it
...
}
class BlogMessage extends Message {
Blog blog
}
Then you can fetch like this...
BlogMessage.list([max:20])
def latestMessages = Message.listOrderByDateCreated(max:20, order:"desc")
: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)