Skip to content

Commit

Permalink
REST: Add new feature to capture optional file extension which gets c…
Browse files Browse the repository at this point in the history
…onverted into 'format' parameter for the purposes of REST calls. This fixes the problems introduced by UrlMappingsFilter.areFileExtensionsEnabled() and the need to globally configure this behavior (it can instead by specified on a mapping by mapping basis)
  • Loading branch information
graemerocher committed May 22, 2013
1 parent 7d3b70d commit 20aa322
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 112 deletions.
Expand Up @@ -73,32 +73,44 @@ private DefaultUrlMappingData(String urlPattern, String[] logicalUrls, String[]
private void parseUrls(List<String> urls, String[] tokens, List<Boolean> optionalTokens) {
StringBuilder buf = new StringBuilder();

for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
for (String t : tokens) {
String token = t.trim();

if (token.equals(SLASH)) continue;
if (token.equals(SLASH)) {
continue;
}

String optionalExtensionPattern = UrlMapping.OPTIONAL_EXTENSION_WILDCARD + '?';
boolean hasOptionalExtension = token.endsWith(optionalExtensionPattern);
String optionalExtension = null;
if(hasOptionalExtension) {
int i = token.indexOf(optionalExtensionPattern);
optionalExtension = token.substring(i, token.length());
token = token.substring(0, i);
}

boolean isOptional = false;
if (token.endsWith(QUESTION_MARK)) {
urls.add(buf.toString());
tokens[i] = token.substring(0, token.length()-1);
buf.append(SLASH).append(tokens[i]);
buf.append(SLASH).append(token);
isOptional = true;
}
else {
} else {
buf.append(SLASH).append(token);
}
if (CAPTURED_WILDCARD.equals(tokens[i])) {
if (CAPTURED_WILDCARD.equals(token)) {
if (isOptional) {
optionalTokens.add(Boolean.TRUE);
}
else {
} else {
optionalTokens.add(Boolean.FALSE);
}
}
if (CAPTURED_DOUBLE_WILDCARD.equals(tokens[i])) {
if (CAPTURED_DOUBLE_WILDCARD.equals(token)) {
optionalTokens.add(Boolean.TRUE);
}

if(optionalExtension != null) {
buf.append(optionalExtension);
}
}
urls.add(buf.toString());
Collections.reverse(urls);
Expand All @@ -124,7 +136,6 @@ public boolean isOptional(int index) {
@Override
public UrlMappingData createRelative(String path) {
Assert.hasLength(path, "Argument [path] cannot be null or blank");
Assert.isTrue(path.startsWith(SLASH), "Argument [path] with value ["+path+"] is not a valid URL. It must start with '/' !");

String newPattern = this.urlPattern + configureUrlPattern(path);

Expand Down
Expand Up @@ -226,7 +226,7 @@ public Object call(Object... args) {
*/
@SuppressWarnings("rawtypes")
class UrlMappingBuilder extends GroovyObjectSupport {
private static final String CAPTURING_WILD_CARD = "(*)";
private static final String CAPTURING_WILD_CARD = UrlMapping.CAPTURED_WILDCARD;
private static final String SLASH = "/";
private static final String CONSTRAINTS = "constraints";

Expand Down Expand Up @@ -342,12 +342,12 @@ public void name(Map<String, UrlMapping> m) {
}

/**
* Define a namespace
* Define a group
*
* @param uri The URI
* @param mappings The mappings
*/
public void namespace(String uri, Closure mappings) {
public void group(String uri, Closure mappings) {

try {
parentResources.push(new ParentResource(null, uri,previousConstraints, true));
Expand Down Expand Up @@ -602,7 +602,7 @@ protected void createResourceRestfulMappings(String controllerName, Object plugi

if(includes.contains(ACTION_INDEX)) {
// GET /$controller -> action:'index'
UrlMapping listUrlMapping = createIndexActionResourcesRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping listUrlMapping = createIndexActionResourcesRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(listUrlMapping);
}

Expand All @@ -614,7 +614,7 @@ protected void createResourceRestfulMappings(String controllerName, Object plugi

if(includes.contains(ACTION_SAVE)) {
// POST /$controller -> action:'save'
UrlMapping saveUrlMapping = createSaveActionResourcesRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping saveUrlMapping = createSaveActionResourcesRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(saveUrlMapping);
}

Expand Down Expand Up @@ -645,17 +645,15 @@ protected void createResourceRestfulMappings(String controllerName, Object plugi
}

protected UrlMapping createDeleteActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData deleteUrlMappingData = urlData.createRelative('/' + CAPTURING_WILD_CARD);
List<ConstrainedProperty> deleteUrlMappingConstraints = new ArrayList<ConstrainedProperty>(previousConstraints);
deleteUrlMappingConstraints.add(new ConstrainedProperty(UrlMapping.class, "id", String.class));
UrlMappingData deleteUrlMappingData = createRelativeUrlDataWithIdAndFormat(urlData);
List<ConstrainedProperty> deleteUrlMappingConstraints = createConstraintsWithIdAndFormat(previousConstraints);

return new RegexUrlMapping(deleteUrlMappingData,controllerName, ACTION_DELETE, null, pluginName, null, HttpMethod.DELETE.toString(),deleteUrlMappingConstraints.toArray(new ConstrainedProperty[deleteUrlMappingConstraints.size()]) , servletContext);
}

protected UrlMapping createUpdateActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData updateUrlMappingData = urlData.createRelative('/' + CAPTURING_WILD_CARD);
List<ConstrainedProperty> updateUrlMappingConstraints = new ArrayList<ConstrainedProperty>(previousConstraints);
updateUrlMappingConstraints.add(new ConstrainedProperty(UrlMapping.class, "id", String.class));
UrlMappingData updateUrlMappingData = createRelativeUrlDataWithIdAndFormat(urlData);
List<ConstrainedProperty> updateUrlMappingConstraints = createConstraintsWithIdAndFormat(previousConstraints);

return new RegexUrlMapping(updateUrlMappingData,controllerName, ACTION_UPDATE, null, pluginName, null, HttpMethod.PUT.toString(),updateUrlMappingConstraints.toArray(new ConstrainedProperty[updateUrlMappingConstraints.size()]) , servletContext);
}
Expand All @@ -669,24 +667,54 @@ protected UrlMapping createEditActionResourcesRestfulMapping(String controllerNa
}

protected UrlMapping createShowActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData showUrlMappingData = urlData.createRelative('/' + CAPTURING_WILD_CARD);
UrlMappingData showUrlMappingData = createRelativeUrlDataWithIdAndFormat(urlData);
List<ConstrainedProperty> showUrlMappingConstraints = createConstraintsWithIdAndFormat(previousConstraints);

return new RegexUrlMapping(showUrlMappingData,controllerName, ACTION_SHOW,null, pluginName, null, HttpMethod.GET.toString(), showUrlMappingConstraints.toArray(new ConstrainedProperty[showUrlMappingConstraints.size()]) , servletContext);
}

private List<ConstrainedProperty> createConstraintsWithIdAndFormat(List<ConstrainedProperty> previousConstraints) {
List<ConstrainedProperty> showUrlMappingConstraints = new ArrayList<ConstrainedProperty>(previousConstraints);
showUrlMappingConstraints.add(new ConstrainedProperty(UrlMapping.class, "id", String.class));
ConstrainedProperty cp = new ConstrainedProperty(UrlMapping.class, "format", String.class);
cp.setNullable(true);
showUrlMappingConstraints.add(cp);
return showUrlMappingConstraints;
}

return new RegexUrlMapping(showUrlMappingData,controllerName, ACTION_SHOW,null, pluginName, null, HttpMethod.GET.toString(), showUrlMappingConstraints.toArray(new ConstrainedProperty[showUrlMappingConstraints.size()]) , servletContext);
private UrlMappingData createRelativeUrlDataWithIdAndFormat(UrlMappingData urlData) {
return urlData.createRelative('/' + CAPTURING_WILD_CARD + UrlMapping.OPTIONAL_EXTENSION_WILDCARD + UrlMapping.QUESTION_MARK);
}
private UrlMappingData createFormatOnlyUrlMappingData(UrlMappingData urlData) {
return urlData.createRelative(UrlMapping.OPTIONAL_EXTENSION_WILDCARD + UrlMapping.QUESTION_MARK);
}

protected UrlMapping createSaveActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
return new RegexUrlMapping(urlData,controllerName, ACTION_SAVE,null, pluginName, null, HttpMethod.POST.toString(),constraintArray, servletContext);

protected UrlMapping createSaveActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> constraints) {
UrlMappingData saveActionUrlMappingData = urlData.createRelative(UrlMapping.OPTIONAL_EXTENSION_WILDCARD + UrlMapping.QUESTION_MARK);
List<ConstrainedProperty> saveUrlMappingConstraints = createFormatOnlyConstraints(constraints);

return new RegexUrlMapping(saveActionUrlMappingData,controllerName, ACTION_SAVE,null, pluginName, null, HttpMethod.POST.toString(),saveUrlMappingConstraints.toArray(new ConstrainedProperty[saveUrlMappingConstraints.size()]), servletContext);
}

protected UrlMapping createCreateActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
UrlMappingData createMappingData = urlData.createRelative("/create");
return new RegexUrlMapping(createMappingData,controllerName, ACTION_CREATE, null, pluginName, null, HttpMethod.GET.toString(), constraintArray, servletContext);
}

protected UrlMapping createIndexActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
return new RegexUrlMapping(urlData, controllerName, ACTION_INDEX, null, pluginName, null, HttpMethod.GET.toString(), constraintArray, servletContext);
protected UrlMapping createIndexActionResourcesRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> constraints) {
UrlMappingData indexActionUrlMappingData = urlData.createRelative(UrlMapping.OPTIONAL_EXTENSION_WILDCARD + UrlMapping.QUESTION_MARK);
List<ConstrainedProperty> indexUrlMappingConstraints = createFormatOnlyConstraints(constraints);

return new RegexUrlMapping(indexActionUrlMappingData, controllerName, ACTION_INDEX, null, pluginName, null, HttpMethod.GET.toString(), indexUrlMappingConstraints.toArray(new ConstrainedProperty[indexUrlMappingConstraints.size()]), servletContext);
}

private List<ConstrainedProperty> createFormatOnlyConstraints(List<ConstrainedProperty> constraints) {
List<ConstrainedProperty> indexUrlMappingConstraints = new ArrayList<ConstrainedProperty>(constraints);
ConstrainedProperty cp = new ConstrainedProperty(UrlMapping.class, "format", String.class);
cp.setNullable(true);
indexUrlMappingConstraints.add(cp);
return indexUrlMappingConstraints;
}

/**
Expand All @@ -711,13 +739,13 @@ protected void createSingleResourceRestfulMappings(String controllerName, Object
if(includes.contains(ACTION_SAVE)) {

// POST /$controller -> action:'save'
UrlMapping saveUrlMapping = createSaveActionResourcesRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping saveUrlMapping = createSaveActionResourcesRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(saveUrlMapping);
}

if(includes.contains(ACTION_SHOW)) {
// GET /$controller -> action:'show'
UrlMapping showUrlMapping = createShowActionResourceRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping showUrlMapping = createShowActionResourceRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(showUrlMapping);
}

Expand All @@ -729,33 +757,44 @@ protected void createSingleResourceRestfulMappings(String controllerName, Object

if(includes.contains(ACTION_UPDATE)) {
// PUT /$controller -> action:'update'
UrlMapping updateUrlMapping = createUpdateActionResourceRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping updateUrlMapping = createUpdateActionResourceRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(updateUrlMapping);
}

if(includes.contains(ACTION_DELETE)) {
// DELETE /$controller -> action:'delete'
UrlMapping deleteUrlMapping = createDeleteActionResourceRestfulMapping(controllerName, pluginName, urlData, constraintArray);
UrlMapping deleteUrlMapping = createDeleteActionResourceRestfulMapping(controllerName, pluginName, urlData, previousConstraints);
configureUrlMapping(deleteUrlMapping);
}

}

protected UrlMapping createDeleteActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
return new RegexUrlMapping(urlData,controllerName,"delete", null, pluginName, null, HttpMethod.DELETE.toString(),constraintArray, servletContext);
protected UrlMapping createDeleteActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData deleteUrlMappingData = createFormatOnlyUrlMappingData(urlData);
List<ConstrainedProperty> deleteUrlMappingConstraints = createFormatOnlyConstraints(previousConstraints);

return new RegexUrlMapping(deleteUrlMappingData,controllerName, ACTION_DELETE, null, pluginName, null, HttpMethod.DELETE.toString(),deleteUrlMappingConstraints.toArray(new ConstrainedProperty[deleteUrlMappingConstraints.size()]) , servletContext);

}

protected UrlMapping createUpdateActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
return new RegexUrlMapping(urlData,controllerName,"update", null, pluginName, null, HttpMethod.PUT.toString(),constraintArray, servletContext);

protected UrlMapping createUpdateActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData updateUrlMappingData = createFormatOnlyUrlMappingData(urlData);
List<ConstrainedProperty> updateUrlMappingConstraints = createFormatOnlyConstraints(previousConstraints);

return new RegexUrlMapping(updateUrlMappingData,controllerName, ACTION_UPDATE, null, pluginName, null, HttpMethod.PUT.toString(),updateUrlMappingConstraints.toArray(new ConstrainedProperty[updateUrlMappingConstraints.size()]) , servletContext);
}

protected UrlMapping createEditctionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
UrlMappingData editMappingData = urlData.createRelative("/edit");
return new RegexUrlMapping(editMappingData,controllerName,"edit", null, pluginName, null, HttpMethod.GET.toString(),constraintArray, servletContext);
return new RegexUrlMapping(editMappingData,controllerName,ACTION_EDIT, null, pluginName, null, HttpMethod.GET.toString(),constraintArray, servletContext);
}

protected UrlMapping createShowActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, ConstrainedProperty[] constraintArray) {
return new RegexUrlMapping(urlData,controllerName,"show",null,pluginName, null, HttpMethod.GET.toString(),constraintArray, servletContext);
protected UrlMapping createShowActionResourceRestfulMapping(String controllerName, Object pluginName, UrlMappingData urlData, List<ConstrainedProperty> previousConstraints) {
UrlMappingData showUrlMappingData = createFormatOnlyUrlMappingData(urlData);
List<ConstrainedProperty> showUrlMappingConstraints = createFormatOnlyConstraints(previousConstraints);

return new RegexUrlMapping(showUrlMappingData,controllerName, ACTION_SHOW,null, pluginName, null, HttpMethod.GET.toString(), showUrlMappingConstraints.toArray(new ConstrainedProperty[showUrlMappingConstraints.size()]) , servletContext);
}

@SuppressWarnings("unchecked")
Expand Down

0 comments on commit 20aa322

Please sign in to comment.