Parsing URL Mappings in Grails

17 / Apr / 2015 by Sandeep Poonia 1 comments

There are times when we need to parse the URL mappings in our grails app. In my case we have a REST API. In which we have implemented a generic query method for all controllers but it is available to only a few using custom URL mappings. We wanted to create test cases in such a way for query action that each time a test case runs it randomly selects some/all endpoints which have query method available. What we could have done is add an entry for each mapping in test case. But considering that there may be more endpoints in near future having query method implemented, we wanted a generic solution for our case.

Grails provides a script url-mappings-report, that prints a summary of all available endpoints in your application. We wanted a similar solution for our case where we can fetch all URL mappings and then identify the mappings with query action and use them in our test case.

We found our answer by looking in the scripts of url-mappings-report. Using the UrlMappingsArtefactHandler we can fetch the list of URL mappings. Code provided below will return mappings for all available actions in all controllers. Code for that would be:

ClassLoader classLoader = this.class.classLoader
def mappings = grailsApplication.getArtefacts(UrlMappingsArtefactHandler.TYPE)

MockServletContext mctx = classLoader.loadClass('org.springframework.mock.web.MockServletContext').newInstance()
DefaultUrlMappingEvaluator evaluator = classLoader.loadClass("org.codehaus.groovy.grails.web.mapping.DefaultUrlMappingEvaluator").newInstance(mctx)
List<UrlMapping> allMappings = []

List<UrlMapping> grailsClassMappings
for (mapping in mappings) {
    if (Script.isAssignableFrom(mapping.getClazz())) {
        grailsClassMappings = evaluator.evaluateMappings(mapping.getClazz())
    } else {
        grailsClassMappings = evaluator.evaluateMappings(mapping.getMappingsClosure())
    }
    allMappings.addAll(grailsClassMappings)
}

After fetching all the mappings we can iterate over them to fetch only the endpoints having query action:

allMappings.findAll { UrlMapping mapping -> return mapping.actionName.equals("query") }

Now we have a list of mappings that have only the query action as endpoint. Now we can use all of them or can use randomly a few in our test.

Complete code would be:

import org.codehaus.groovy.grails.commons.UrlMappingsArtefactHandler
import org.codehaus.groovy.grails.web.mapping.DefaultUrlMappingEvaluator
import org.codehaus.groovy.grails.web.mapping.UrlMapping
import org.springframework.mock.web.MockServletContext

private List<UrlMapping> getAllURLMappings(){
    ClassLoader classLoader = this.class.classLoader
    def mappings = grailsApplication.getArtefacts(UrlMappingsArtefactHandler.TYPE)

    MockServletContext mctx = classLoader.loadClass('org.springframework.mock.web.MockServletContext').newInstance()
    DefaultUrlMappingEvaluator evaluator = classLoader.loadClass("org.codehaus.groovy.grails.web.mapping.DefaultUrlMappingEvaluator").newInstance(mctx)
    List<UrlMapping> allMappings = []

    List<UrlMapping> grailsClassMappings
    for (mapping in mappings) {
        if (Script.isAssignableFrom(mapping.getClazz())) {
            grailsClassMappings = evaluator.evaluateMappings(mapping.getClazz())
        } else {
            grailsClassMappings = evaluator.evaluateMappings(mapping.getMappingsClosure())
        }
        allMappings.addAll(grailsClassMappings)
    }
    return allMappings
}

private List<String> getMappingForAction(List<UrlMapping> mappings, String action){
    return mappings.findAll {
        UrlMapping mapping ->
            return mapping.actionName.equals(action)
    }*.urlData.urlPattern
}

List<UrlMapping> allMappings = getAllURLMappings()
List<String> mappings = getMappingForAction(allMappings, "query")

int size = mappings.size()

Random r = new Random()
int index = r.nextInt(size - 0)

println mappings[index]

FOUND THIS USEFUL? SHARE IT

comments (1 “Parsing URL Mappings in Grails”)

Leave a comment -