Grails tip: refactoring your URLs

On the current project I am working we use a lot of integration tests. For you non-Grails users out there, Integration tests test your Controller APIs, your Services and any persistence that might happen all very neatly. The only slice of the cake they don’t test from a back end perspective are your Grails filters for which you’d need something like a functional test. In the Grails world, Controller API’s are mapped to URL requests in the URLMappings.groovy file. This is just a simple Groovy to configure which HTTP request go to which Controller.

 
 
 
For example:

class UrlMappings {
    static mappings = {
        "/sports/rugby/ball" (controller: "rugbyBall", action = [POST: "createBall", DELETE: "removeBall", GET: "getBall"])
        ...

So in the above example, the HTTP request /sports/rugby/ball will go to the RugbyBallController and will go to the methods: createBall(), deleteBall(), getBall() depending on weather the request is a GET, POST or DELETE. Now suppose, you have your project all set up to server up the CRUD operations for the rugby ball and after a few hectic sprints some software entropy creeps and you need to refactor your Controller APIs but before you race ahead and do that your project manager looks you in the eye and says: “You must support all existing APIs as clients are using them”.
This is how generally refactoring works in the real world when things go into production. There is always a phase of supporting the old and new, deprecating the old and then when everyone is happy removing it. Anyway, you begin by updating your URLMappings.groovy

class UrlMappings {
    static mappings = {
        // Old APIs
        "/sports/rugby/ball" ( controller: "rugbyBall", action = [POST: "oldCreateBall", DELETE: "oldRemoveBall", GET: "oldGetBall"])
        ...

        // New APIs
        "/sports/rugby/v2/ball" ( controller: "rugbyBall", action = [POST: "createBall", DELETE: "removeBall", GET: "getBall"])
        ...

The URLMappings.groovy show the old and the new. The old APIs are going to controller methods that you have renamed. Clients using these APIs are not impacted because they only send HTTP requests, they do not know which Controller is behind these endpoints. The old APIs already have really good integration tests and our project manager has mandated that the new APIs must have similar quality integration tests before they go anywhere near pre-production.

def "test adding a single item to your cart"() {
    setup: "Set up the Cart and Landing Controller"
       //...
    when:
       //...
       rugbyBallController.oldGetBall();
       rugbyBall = JSON.parse(rugbyBallController.response.contentAsString)
    then:
       rugbyBall.isOval();

Mr. Project manager says: “I want all these new tests added by Friday or you are not going for a pint after work. You need a quick way to get your integration tests done”. Thinking about that cool lager and its quenching effect on the back of your throat, you remember Groovy’s excellent support for invoking methods dynamically where you can specify the name of the method as a variable.

myObject."$myMethod"()  // myMethod is a Groovy String variable.

In the above code snippet, myMethod is a variable that corresponds to the name of method you want to invoke on myObject. “$myMethod” means, evaluate the variable myMethod (which of course will be the method name), the () of course just means invokes the method. Eureka moments happens when you remember that the old and new APIs will return the exact same JSON. All you need to do is run the same test twice, once for the old code and once for the new. Since you are using the spock framework for your integration tests that’s easily achieved using a where block.

def "test adding a single item to your cart"(String method) {
    setup: "Set up the Cart and Landing Controller"
       //...
    when:
       //...
       rugbyBallController."$method"();
       rugbyBall = JSON.parse(rugbyBallController.response.contentAsString)
    then:
       rugbyBall.isOval();
    where:
       method = ["oldGetBall", "getBall"]

Happy days. Now go off and drink that lager.

Reference: Grails tip: refactoring your URLs from our JCG partner Alex Staveley at the Dublin’s Tech Blog blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


6 − four =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close