So I have finished up the basic API for our user management system in grails so it is now time to switch gears and start working on the client. I decided to go with groovy (hey I need to hone in my skills =P) so I went off to find out how other people were attaching onto RESTful apis. Well it seems that most people are using the RESTClient found in the HTTPBuilder library. It has WAY more dependencies than I would like to have but I will worry about that later. Here is the first consuming method I have written using the client.
1 2 3 4 5 6 7 8 9 10 11 12 13
| def static list(appId, accessTypeCode) {
def accessLevels = []
def client = new RESTClient("http://localhost:8080/AppHub/accessLevel/${appId}/${accessTypeCode}")
def resp = client.get(contentType:ContentType.XML, query:[format:'xml'])
resp.data.children().each { list ->
def accessLevel = new AccessLevel()
list.children().each { child ->
accessLevel.setProperty(child.name, child.text())
}
accessLevels.add(accessLevel)
}
return accessLevels
} |
1
| println AccessLevel.list('LEGALDOC', 'ACCESS_LVL').size() |
Pretty straight forward and the code is much shorter than it would be in Java (or heck even in Groovy) if I needed to manually setup all of the HTTP/URL connection and parse the response back.
Notice that I am using some metaprogramming to dynamically build AccessLevel objects from the responding XML. This works for this particular example and will continue to work for most of the API. However, if I ever return a deeper xml tree (User -> List of some object -> object) It will take a bit more finesse.
Posted in: REST, groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| import grails.test.*
import grails.converters.XML;
import grails.converters.JSON;
class UserControllerTests extends GroovyTestCase {
void testGETXMLResponse() {
def controller = new UserController()
def existingUser = new User(username: "fred", password: User.encrypt("letmein"), firstName: "Fred", lastName: "Flintstone", middleName: "T", phone: "555-555-5555", email: 'fred@flintstone.com', activationDate: new Date(), logonFailureCount: 0).save()
controller.request.method = 'GET'
controller.request.contentType = 'text/xml'
controller.params.format = 'xml'
controller.params.id = existingUser.id
controller.show()
def user = new XmlSlurper().parseText(controller.response.contentAsString)
assert user.@id == existingUser.id //we have to use the .@id because id is stored as an attribute to the user tag
assert user.phone.text() == existingUser.phone //we have to use phone.text() because it is stored as a value of the phone tag.
}
void testGETJSONResponse() {
def controller = new UserController()
def existingUser = new User(username: "wilma", password: User.encrypt("letmein"), firstName: "Wilma", lastName: "Flintstone", middleName: "T", phone: "555-555-5555", email: 'wilma@flintstone.com', activationDate: new Date(), logonFailureCount: 0).save()
controller.request.method = 'GET'
controller.request.contentType = 'text/json'
controller.params.format = 'json'
controller.params.id = existingUser.id
controller.show()
def user = JSON.parse(controller.response.contentAsString)
assert user.id == existingUser.id //this is how we access json text
assert user.phone == existingUser.phone
}
void testPUTXMLResponse() {
def controller = new UserController()
def existingUser = new User(username: "dino", password: User.encrypt("letmein"), firstName: "Dino", lastName: "Flintstone", middleName: "T", phone: "555-555-5555", email: 'dino@flintstone.com', activationDate: new Date(), logonFailureCount: 0).save()
existingUser.phone = '555-555-1111'
def xml = existingUser as XML
controller.request.method = 'PUT'
controller.request.contentType = 'text/xml'
controller.params.format = 'xml'
controller.request.content = xml.toString().getBytes()
controller.request.getAttribute("org.codehaus.groovy.grails.WEB_REQUEST").informParameterCreationListeners()
controller.params.id = existingUser.id
controller.update()
def user = XML.parse(controller.response.contentAsString)
assert user.@id == existingUser.id
assert user.phone.text() == existingUser.phone
}
// I have submitted a JIRA issue regarding this test not working. http://jira.codehaus.org/browse/GRAILS-5585
/*
void testPUTJSONResponse() {
def controller = new UserController()
def pebbles = new User(username:"pebbles", password:User.encrypt("letmein"), firstName:"Pebbles", lastName:"Flintstone", middleName:"T", phone:"555-555-5555", email:'pebbles@flintstone.com', activationDate:new Date(), logonFailureCount:0, deactivationDate:null).save()
def builder = new JSONBuilder()
def result = builder.build { user = pebbles }
controller.request.method = 'PUT'
controller.request.contentType = 'application/json'
controller.params.format = 'json'
controller.request.content = result.toString().getBytes()
controller.request.getAttribute("org.codehaus.groovy.grails.WEB_REQUEST").informParameterCreationListeners()
controller.params.id = '4'
controller.update()
def user = JSON.parse(controller.response.contentAsString)
assert user.id == 4
assert user.phone == '555-555-1111'
}
*/
void testPOSTXMLResponse() {
def controller = new UserController()
def existingUser = new User(username: "barney", password: User.encrypt("letmein"), firstName: "Barney", lastName: "Rubble", middleName: "T", phone: "555-555-5555", email: 'barney@rubble.com', activationDate: new Date(), logonFailureCount: 0)
def xml = existingUser as XML
controller.request.method = 'POST'
controller.request.contentType = 'text/xml'
controller.params.format = 'xml'
controller.request.content = xml.toString().getBytes()
controller.request.getAttribute("org.codehaus.groovy.grails.WEB_REQUEST").informParameterCreationListeners()
controller.create()
def user = XML.parse(controller.response.contentAsString)
assert user.username.text() == "barney"
}
// I believe this is not working due to the same bug for the PUT test. http://jira.codehaus.org/browse/GRAILS-5585
/*
void testPOSTJSONResponse() {
def controller = new UserController()
controller.request.method = 'POST'
controller.request.contentType = 'text/json'
controller.params.format = 'json'
controller.request.content = '{"activationDate":"2009-12-09T15:28:37Z","activeDirectoryUsername":null,"class":"User","createdBy":null,"deactivationDate":null,"disabled":false,"email":"betty@rubble.com","firstName":"Betty","id":1,"lastAccessDate":null,"lastName":"Rubble","lastUpdatedBy":null,"lastUpdatedDate":null,"logonFailureCount":0,"middleName":"T","mustChangePassword":false,"password":"FtPJCHN","phone":"555-555-5555","useActiveDirectory":false,"username":"betty","version":null}'.getBytes()
controller.create()
def user = JSON.parse(controller.response.contentAsString)
println controller.response.contentAsString
assert user.id == 5
assert user.username == "betty"
}
*/
void testDELETEResponse() {
def controller = new UserController()
def existingUser = new User(username: "fred", password: User.encrypt("letmein"), firstName: "Fred", lastName: "Flintstone", middleName: "T", phone: "555-555-5555", email: 'fred@flintstone.com', activationDate: new Date(), logonFailureCount: 0).save()
controller.request.method = 'DELETE'
controller.params.id = existingUser.id
controller.delete()
assert controller.response.status == 200
}
} |
Posted in: REST, grails, groovy
So I have run into more problems testing my restful services. However, it seems to stem from the fact that unit tests do not provide all the necessary tools for testing the services properly. Below is my new integration tests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void testGETXMLResponse() {
def controller = new UserController()
controller.request.method = 'GET'
controller.request.contentType = 'text/xml'
controller.params.format = 'xml'
controller.params.id = '1'
controller.show()
def getXMLUser = XML.parse(controller.response.contentAsString)
assert getXMLUser.user.id == 1
assert getXMLUser.user.username == "fred"
assert getXMLUser.user.password == User.encrypt("letmein")
} |
Posted in: REST, grails, groovy
Just a quick example on how you can test the various HTTP methods for your Grails controllers.
Controller:
1 2 3 4 5 6 7 8 9 10 11 12
| def show = {
def user = User.get(params.id)
if(user) {
withFormat {
html user:user
xml { render user as XML }
json { render user as JSON }
}
} else {
response.sendError 404
}
} |
Unit Tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void testXMLResponse() {
mockDomain(User, [new User(id:1, username:"fred", password:User.encrypt("letmein"))])
controller.request.method = 'GET'
controller.request.contentType = 'text/xml'
controller.request.format = 'xml'
controller.params.id = '1'
controller.show()
def user = XML.parse(controller.response.getContentAsString())
assert user.id == 1
assert user.username == "fred"
assert user.password == User.encrypt("letmein")
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void testJSONResponse() {
mockDomain(User, [new User(id:1, username:"fred", password:User.encrypt("letmein"))])
controller.request.method = 'GET'
controller.request.contentType = 'text/json'
controller.request.format = 'json'
controller.params.id = '1'
controller.show()
def user = JSON.parse(controller.response.getContentAsString())
println controller.response.contentAsString
assert user.id == 1
assert user.username == "fred"
assert user.password == User.encrypt("letmein")
} |
Pretty simple!
Posted in: REST, grails, groovy
I hate telling people I am a programmer… I do so much more than program. I am an architect, a database administrator, a system administrator, a quality control expert, a customer support representative, and THEN I am a programmer. Heck I am probably more than just those things I listed – but you get the point… Why do people insist on calling us programmers? Why am I not called a Software ENGINEER!!!!
Posted in: software engineer
- The ability to automatically update my local library based on an external storage system (possibly only add, not remove).
- Integration with Last.FM, Pandora, Sirius/XM for streaming music from their services.
- The ability to arrange my music locally in any way I wish (Based on meta info would be nice).
- The ability to fix missing META information from my mp3s, and even standardize META information automatically.
- Smart playlists that are updated based on the location of songs within a particular directory. Basically you create a playlist and say add anything that gets added to this directory or any child directories.
I am sure there is more. Will update when I think of them.
Posted in: Uncategorized
Josh Fraser has written an excellent post on TechCruch about the newly emerging battle between rssCloud and PubSubHubbub (which has now been shortened to PuSH). I agree with his assessment that PuSH is poised to become the winner in the battle because of its over whelming advantages over rssCloud. Anyways check out the article.
Posted in: Uncategorized
This has been passed around, so thought I would share:
All in all, it hadn’t been a good day. Bad traffic, a malfunctioning
computer, incompetent coworkers and a sore back all made me a seething
cauldron of rage. But more importantly for this story, it had been over
forty-eight hours since I’d last taken a dump. I’d tried to jumpstart
the process, beginning my day with a bowl of bowel-cleansing fiber
cereal, following it with six cups of coffee at work, and adding a
bean-laden lunch at Taco Bell. As I was returning home from work, my
insides let me know with subtle rumbles and the emission of the
occasional tiny fart that Big Things would be happening soon. Alas, I
had to stop at the mall to pick up an order for my wife. I completed
this task, and as I was walking past the stores on my way backto the
car, I noticed a large sale sign proclaiming, “Everything Must Go!” This
was prophetic, for my colon informed me with a sudden violent cramp and
a wet, squeaky fart that everything was indeed about to go. I hurried to
the mall bathrooms. I surveyed the five stalls, which I have numbered 0
through 4 (I write a lot of software) for your convenience:
0.Occupied.
1.Clean, but Bathroom Protocol forbids its use, as it’s next to the
occupied one.
2.Poo on seat.
3.Poo and toilet paper in bowl, unidentifiable liquid splattered on
seat.
4.No toilet paper, no stall door, unidentifiable sticky object near base
of toilet.
Clearly, it had to be Stall ..1. I trudged back, entered, dropped trou
and sat down. I’m normally a fairly Shameful Sh1tter. I wasn’t happy
about being next to the occupied stall, but Big Things were afoot.
I was just getting ready to bear down when all of a sudden the sweet
sounds of Beethoven came from next door, followed by a fumbling, and
then the sound of a voice answering the ringing phone. As usual for a
cell phone conversation, the voice was exactly 8 dB louder than it
needed to be. Out of Shameful habit, my sphincter slammed shut. The
inane conversation went on and on. Mr. Sh1tter was blathering to Mrs.
Sh1tter about the sh1tty day he had. I sat there, cramping and
miserable, waiting for him to finish. As the loud conversation dragged
on, I became angrier and angrier, thinking that I, too, had a crappy
day, but I was too polite to yak about in public. My bowels let me know
in no uncertain terms that if I didn’t get crapping soon, my day would
be getting even crappier.
Finally my anger reached a point that overcame Shamefulness. I no longer
cared. I gripped the toilet paper holder with one hand, braced my other
hand against the side of the stall, and pushed with all my might. I was
rewarded with a fart of colossal magnitude — a cross between the sound
of someone ripping a very wet bed sheet in half and of plywood being
torn off a wall. The sound gradually transitioned into a heavily
modulated low-RPM tone, not unlike someone firing up a Harley. I managed
to hit the resonance frequency of the stall, and it shook gently.
Once my @ss cheeks stopped flapping in the breeze, three things became
apparent:
(1) The next-door conversation had ceased;
(2) my colon’s continued seizing indicated that there was more to come;
and
(3) the bathroom was now beset by a horrible, eldritch stench.
It was as if a gateway to Hell had been opened. The foul miasma quickly
made its way underthe stall and began choking my poop-mate. This initial
“herald” fart had ended his conversation in mid-sentence.
“Oh my God,” I heard him utter, following it with suppressed sounds of
choking, and then, “No, baby, that wasn’t me (cough, gag), you could
hear that (gag)??”
Now there was no stopping me. I pushed for all I was worth. I could
swear that in the resulting cacophony of rips, squirts, splashes, poots,
and blasts, I was actually lifted slightly off the pot. The amount of
stuff in me was incredible. It sprayed against the bowl with tremendous
force. Later, in surveying the damage, I’d see that liquid poop had
actually managed to ricochet out of the bowl and run down the side on to
the floor. But for now, all I could do was hang on for the ride.
Next door I could hear him fumbling with the paper dispenser as he
desperately tried to finish his task. Little snatches of conversation
made themselves heard over my anal symphony: “Gotta go… horrible…
throw up… in my mouth… not… make it… tell the kids… love
them… oh God…” followed by more sounds of suppressed gagging and
retching.
Alas, it is evidently difficult to hold one’s phone and wipe one’s bum
at the same time. Just as my high-pressure abuse of the toilet was
winding down, I heard a plop and splash from next door, followed by
string of swear words and gags. My poop-mate had dropped his phone into
the toilet.
There was a lull in my production, and the restroom became deathly
quiet. I could envision him standing there, wondering what to do. A
final anal announcement came trumpeting from my behind, small chunks
plopping noisily into the water. That must have been the last straw. I
heard a flush, a fumbling with the lock, and then the stall door was
thrown open. I heard him running out of the bathroom, slamming the door
behind him.
After a considerable amount of paperwork, I got up and surveyed the
damage. I felt bad for the janitor who’d be forced to deal with this,
but I knew that flushing was not an option. No toilet in the world could
handle that unholy mess. Flushing would only lead to a floor flooded
with filth.
As I left, I glanced into the next-door stall. Nothing remained in the
bowl. Had he flushed his phone, or had he plucked it out and left the
bathroom with nasty unwashed hands? The world will never know.
I exited the bathroom, momentarily proud and Shameless, looking around
for a face glaring at me. But I saw no one. I suspect that somehow my
supernatural elimination has managed to transfer my Shamefulness to my
anonymous poop-mate. I think it’ll be a long time before he can bring
himself to poop in public — and I doubt he’ll ever again answer his
cell phone in the loo. And this, my friends, is why you should never
talk on your phone in the bathroom.”
Posted in: Uncategorized
Jud Valeski (CTO @Gnip) wrote an interesting post a few days ago that has got me thinking about web services and how we use them in our daily lives. The post talks about the need for a web service layer that allows for throttling/blocking of api calls on something other than an ip address.
He suggests that instead of polling these public facing web services, we should develop some type of PUSH based web service layer (basically a big Gnip type system). Now what he has not answered yet is whether that system should be built on top of SOAP and REST or whether it should be built from the ground up.
All I know is that this is definitely one interesting problem.
Posted in: amazon, cloud computing, push, web services
I have often thought that cloud computing was a really awesome idea, but that enterprise adoption was going to be really hard. You do not own the servers you are installing the applications on, you do not own the databases that store the data, and how do you protect it from being abused? Furthermore, what about public records requests? Well now Amazon has released details of a new service called VPC or Virtual Private Cloud. There are pleanty of details at Wener Vogel’s blog (Amazon’s CTO). Check it out.


Posted in: amazon, cloud computing
Tags: amazon, cloud, cloud computing, vpc