Merged searchapi (5.2.1) to 5.2.N (5.2.1)

129774 gjames: SEARCH-113: Moving more api logic to helper classes, using a "trait" style


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130165 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gethin James
2016-09-06 14:14:29 +00:00
parent ff2ce9d706
commit bfd32ef859
26 changed files with 1555 additions and 1303 deletions

View File

@@ -37,6 +37,7 @@ import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper.Writer;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.codehaus.jackson.JsonGenerationException;
@@ -48,7 +49,7 @@ import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
public class NetworkWebScriptGet extends ApiWebScript
public class NetworkWebScriptGet extends ApiWebScript implements ResponseWriter
{
private Networks networks;
private ResourceWebScriptHelper helper;
@@ -108,11 +109,11 @@ public class NetworkWebScriptGet extends ApiWebScript
}
catch (ApiException | WebScriptException apiException)
{
assistant.renderException(apiException, res);
renderException(apiException, res, assistant);
}
catch (RuntimeException runtimeException)
{
assistant.renderException(runtimeException, res);
renderException(runtimeException, res, assistant);
}
}
}

View File

@@ -39,6 +39,8 @@ import org.alfresco.rest.framework.jacksonextensions.JacksonHelper.Writer;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.codehaus.jackson.JsonGenerationException;
@@ -56,7 +58,7 @@ import org.springframework.extensions.webscripts.WebScriptResponse;
* @author steveglover
*
*/
public class NetworksWebScriptGet extends ApiWebScript
public class NetworksWebScriptGet extends ApiWebScript implements RecognizedParamsExtractor, ResponseWriter
{
private Networks networks;
private ResourceWebScriptHelper helper;
@@ -82,7 +84,7 @@ public class NetworksWebScriptGet extends ApiWebScript
@Override
public Void execute() throws Throwable
{
final Paging paging = ResourceWebScriptHelper.findPaging(req);
final Paging paging = findPaging(req);
// apply content type
res.setContentType(Format.JSON.mimetype() + ";charset=UTF-8");
@@ -116,11 +118,11 @@ public class NetworksWebScriptGet extends ApiWebScript
}
catch (ApiException | WebScriptException apiException)
{
assistant.renderException(apiException, res);
renderException(apiException, res, assistant);
}
catch (RuntimeException runtimeException)
{
assistant.renderException(runtimeException, res);
renderException(runtimeException, res, assistant);
}
}
}

View File

@@ -33,12 +33,13 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.web.scripts.TenantWebScriptServletRuntime;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.springframework.extensions.config.ServerProperties;
import org.springframework.extensions.surf.util.URLDecoder;
import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory;
public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServletRuntime
public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServletRuntime implements ResponseWriter
{
private static final Pattern CMIS_URI_PATTERN = Pattern.compile(".*/cmis/versions/[0-9]+\\.[0-9]+/.*");
private ApiAssistant apiAssistant;
@@ -142,7 +143,7 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl
else
{
try {
apiAssistant.renderException((Exception)exception, response);
renderException((Exception)exception, response, apiAssistant);
} catch (IOException e) {
logger.error("Internal error", e);
throw new WebScriptException("Internal error", e);

View File

@@ -32,6 +32,7 @@ import org.alfresco.rest.api.model.Person;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
/**
@@ -44,9 +45,9 @@ public interface Queries
{
// General
static String PARAM_TERM = "term";
static String PARAM_ORDERBY = ResourceWebScriptHelper.PARAM_ORDERBY;
static String PARAM_FIELDS = ResourceWebScriptHelper.PARAM_FILTER_FIELDS;
static String PARAM_INCLUDE = ResourceWebScriptHelper.PARAM_INCLUDE;
static String PARAM_ORDERBY = RecognizedParamsExtractor.PARAM_ORDERBY;
static String PARAM_FIELDS = RecognizedParamsExtractor.PARAM_FILTER_FIELDS;
static String PARAM_INCLUDE = RecognizedParamsExtractor.PARAM_INCLUDE;
// Node query
static String PARAM_ROOT_NODE_ID = "rootNodeId";

View File

@@ -57,7 +57,7 @@ import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
@@ -107,7 +107,7 @@ import java.util.Set;
*
* @since publicapi1.0
*/
public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
public class QuickShareLinksImpl implements QuickShareLinks, RecognizedParamsExtractor, InitializingBean
{
private static final Log logger = LogFactory.getLog(QuickShareLinksImpl.class);
@@ -404,7 +404,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean
// hmm ... can we simplify ?
String filterStatusCreated = "(" + Renditions.PARAM_STATUS + "='" + Rendition.RenditionStatus.CREATED + "')";
Query whereQuery = ResourceWebScriptHelper.getWhereClause(filterStatusCreated);
Query whereQuery = getWhereClause(filterStatusCreated);
Params.RecognizedParams recParams = new Params.RecognizedParams(null, null, null, null, null, null, whereQuery, null, false);
Parameters params = Params.valueOf(recParams, null, null, null);

View File

@@ -29,6 +29,7 @@ import java.io.IOException;
import org.alfresco.rest.framework.resource.SerializablePagedCollection;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
@@ -90,8 +91,8 @@ public class SerializerOfCollectionWithPaging extends SerializerBase<Serializabl
}
if (pagedCol.getPaging() != null)
{
jgen.writeNumberField(ResourceWebScriptHelper.PARAM_PAGING_SKIP, pagedCol.getPaging().getSkipCount());
jgen.writeNumberField(ResourceWebScriptHelper.PARAM_PAGING_MAX, pagedCol.getPaging().getMaxItems());
jgen.writeNumberField(RecognizedParamsExtractor.PARAM_PAGING_SKIP, pagedCol.getPaging().getSkipCount());
jgen.writeNumberField(RecognizedParamsExtractor.PARAM_PAGING_MAX, pagedCol.getPaging().getMaxItems());
}
jgen.writeEndObject();
}

View File

@@ -56,34 +56,16 @@ public class ApiAssistant {
private static Log logger = LogFactory.getLog(ApiAssistant.class);
public final static String UTF8 = "UTF-8";
public final static ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(),UTF8, 0, null);
public final static Cache CACHE_NEVER = new Cache(new RequiredCache()
{
@Override
public boolean getNeverCache()
{
return true;
}
@Override
public boolean getIsPublic()
{
return false;
}
@Override
public boolean getMustRevalidate()
{
return true;
}
});
private ExceptionResolver<Exception> defaultResolver = new DefaultExceptionResolver();
private ExceptionResolver<WebScriptException> webScriptExceptionResolver;
private ExceptionResolver<Exception> resolver;
private JacksonHelper jsonHelper;
/**
* Determines the api being used from the templateVars
* @param templateVars
* @return Api
*/
public static Api determineApi(Map<String, String> templateVars)
{
String apiScope = templateVars.get("apiScope");
@@ -92,6 +74,11 @@ public class ApiAssistant {
return Api.valueOf(apiName,apiScope,apiVersion);
}
/**
* Resolves an exception as a json error.
* @param exception
* @return ErrorResponse
*/
public ErrorResponse resolveException(Exception ex)
{
ErrorResponse error = null;
@@ -110,99 +97,6 @@ public class ApiAssistant {
return error;
}
/**
* Sets the response headers with any information we know about the content
* @param res WebScriptResponse
* @param contentInfo Content Information
*/
public void setContentInfoOnResponse(WebScriptResponse res, ContentInfo contentInfo)
{
if (contentInfo != null)
{
//Set content info on the response
res.setContentType(contentInfo.getMimeType());
res.setContentEncoding(contentInfo.getEncoding());
if (res instanceof WrappingWebScriptResponse)
{
WrappingWebScriptResponse wrappedRes = ((WrappingWebScriptResponse) res);
res = wrappedRes.getNext();
}
if (res instanceof WebScriptServletResponse)
{
WebScriptServletResponse servletResponse = (WebScriptServletResponse) res;
if (contentInfo.getLength() > 0)
{
if (contentInfo.getLength() > 0 && contentInfo.getLength() < Integer.MAX_VALUE)
{
servletResponse.getHttpServletResponse().setContentLength((int) contentInfo.getLength());
}
}
if (contentInfo.getLocale() != null)
{
servletResponse.getHttpServletResponse().setLocale(contentInfo.getLocale());
}
}
}
}
/**
* Renders an exception to the output stream as Json.
* @param exception
* @param response
* @throws IOException
*/
public void renderException(Exception exception, final WebScriptResponse response) throws IOException {
renderErrorResponse(resolveException(exception), response);
}
/**
* Renders a JSON error response
* @param errorResponse The error
* @param res web script response
* @throws IOException
*/
public void renderErrorResponse(ErrorResponse errorResponse, final WebScriptResponse res) throws IOException {
String logId = "";
if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || logger.isDebugEnabled())
{
logId = org.alfresco.util.GUID.generate();
logger.error(logId+" : "+errorResponse.getStackTrace());
}
String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);
final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(),
errorResponse.getStatusCode(),
errorResponse.getBriefSummary(),
stackMessage,
logId,
errorResponse.getAdditionalState(),
DefaultExceptionResolver.ERROR_URL);
setContentInfoOnResponse(res, DEFAULT_JSON_CONTENT);
// Status must be set before the response is written by Jackson (which will by default close and commit the response).
// In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
res.setStatus(errorToWrite.getStatusCode());
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
{
@SuppressWarnings("unchecked")
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
throws JsonGenerationException, JsonMappingException, IOException
{
JSONObject obj = new JSONObject();
obj.put("error", errorToWrite);
objectMapper.writeValue(generator, obj);
}
});
}
public JacksonHelper getJsonHelper() {
return jsonHelper;
}

View File

@@ -0,0 +1,451 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.tools;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.resource.parameters.InvalidSelectException;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException;
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryImpl;
import org.alfresco.rest.framework.resource.parameters.where.WhereCompiler;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonErrorNode;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.RewriteCardinalityException;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.webscripts.WebScriptRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/*
* Extracts recognized parameters from the HTTP request.
*
* @author Gethin James
*/
public interface RecognizedParamsExtractor
{
public static final String PARAM_RELATIONS = "relations";
public static final String PARAM_FILTER_FIELDS = "fields";
@Deprecated
public static final String PARAM_FILTER_PROPERTIES = "properties";
public static final String PARAM_PAGING_SKIP = "skipCount";
public static final String PARAM_PAGING_MAX = "maxItems";
public static final String PARAM_ORDERBY = "orderBy";
public static final String PARAM_WHERE = "where";
public static final String PARAM_SELECT = "select";
public static final String PARAM_INCLUDE = "include";
public static final String PARAM_INCLUDE_SOURCE_ENTITY = "includeSource";
public static final List<String> KNOWN_PARAMS = Arrays.asList(
PARAM_RELATIONS, PARAM_FILTER_PROPERTIES, PARAM_FILTER_FIELDS,PARAM_PAGING_SKIP,PARAM_PAGING_MAX,
PARAM_ORDERBY, PARAM_WHERE, PARAM_SELECT, PARAM_INCLUDE_SOURCE_ENTITY);
default Log rpeLogger() {
return LogFactory.getLog(this.getClass());
}
/**
* Finds the formal set of params that any rest service could potentially have passed in as request params
* @param req WebScriptRequest
* @return RecognizedParams a POJO containing the params for use with the Params objects
*/
default Params.RecognizedParams getRecognizedParams(WebScriptRequest req)
{
Paging paging = findPaging(req);
List<SortColumn> sorting = getSort(req.getParameter(PARAM_ORDERBY));
Map<String, BeanPropertiesFilter> relationFilter = getRelationFilter(req.getParameter(PARAM_RELATIONS));
Query whereQuery = getWhereClause(req.getParameter(PARAM_WHERE));
Map<String, String[]> requestParams = getRequestParameters(req);
boolean includeSource = Boolean.valueOf(req.getParameter(PARAM_INCLUDE_SOURCE_ENTITY));
List<String> includedFields = getIncludeClause(req.getParameter(PARAM_INCLUDE));
List<String> selectFields = getSelectClause(req.getParameter(PARAM_SELECT));
String fields = req.getParameter(PARAM_FILTER_FIELDS);
String properties = req.getParameter(PARAM_FILTER_PROPERTIES);
if ((fields != null) && (properties != null))
{
if (rpeLogger().isWarnEnabled())
{
rpeLogger().warn("Taking 'fields' param [" + fields + "] and ignoring deprecated 'properties' param [" + properties + "]");
}
}
BeanPropertiesFilter filter = getFilter((fields != null ? fields : properties), includedFields);
return new Params.RecognizedParams(requestParams, paging, filter, relationFilter, includedFields, selectFields, whereQuery, sorting, includeSource);
}
/**
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
*
* Parses the parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
* filter param is set then a default BeanFilter is returned that will never
* filter fields (ie. Returns all bean properties).
*
* If selectList is provided then it will take precedence (ie. be included) over the fields/properties filter
* for top-level entries (bean properties).
*
* For example, this will return entries from both select & properties, eg.
*
* select=abc,def&properties=id,name,ghi
*
* Note: it should be noted that API-generic "fields" clause does not currently work for sub-entries.
*
* Hence, even if the API-specific "select" clause allows selection of a sub-entries this cannot be used
* with "fields" filtering. For example, an API-specific method may implement and return "abc/blah", eg.
*
* select=abc/blah
*
* However the following will not return "abc/blah" if used with fields filtering, eg.
*
* select=abc/blah&fields=id,name,ghi
*
* If fields filtering is desired then it would require "abc" to be selected and returned as a whole, eg.
*
* select=abc&fields=id,name,ghi
*
* @param filterParams
* @param selectList
* @return
*/
default BeanPropertiesFilter getFilter(String filterParams, List<String> selectList)
{
if (filterParams != null)
{
StringTokenizer st = new StringTokenizer(filterParams, ",");
Set<String> filteredProperties = new HashSet<String>(st.countTokens());
while (st.hasMoreTokens())
{
filteredProperties.add(st.nextToken());
}
// if supplied, the select takes precedence over the filter (fields/properties) for top-level bean properties
if (selectList != null)
{
for (String select : selectList)
{
String[] split = select.split("/");
filteredProperties.add(split[0]);
}
}
rpeLogger().debug("Filtering using the following properties: " + filteredProperties);
BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
return filter;
}
return BeanPropertiesFilter.ALLOW_ALL;
}
/**
* Takes the "select" parameter and turns it into a List<String> property names
* @param selectParam String
* @return bean property names potentially using JSON Pointer syntax
*/
@SuppressWarnings("unchecked")
@Deprecated
default List<String> getSelectClause(String selectParam) throws InvalidArgumentException
{
return getClause(selectParam, "SELECT");
}
/**
* Takes the "include" parameter and turns it into a List<String> property names
* @param includeParam String
* @return bean property names potentially using JSON Pointer syntax
*/
@SuppressWarnings("unchecked")
default List<String> getIncludeClause(String includeParam) throws InvalidArgumentException
{
return getClause(includeParam, "INCLUDE");
}
/**
* Gets the clause specificed in paramName
* @param param
* @param paramName
* @return bean property names potentially using JSON Pointer syntax
*/
default List<String> getClause(String param, String paramName)
{
if (param == null) return Collections.emptyList();
try {
CommonTree selectedPropsTree = WhereCompiler.compileSelectClause(param);
if (selectedPropsTree instanceof CommonErrorNode)
{
rpeLogger().debug("Error parsing the "+paramName+" clause "+selectedPropsTree);
throw new InvalidSelectException(paramName, selectedPropsTree);
}
if (selectedPropsTree.getChildCount() == 0 && !selectedPropsTree.getText().isEmpty())
{
return Arrays.asList(selectedPropsTree.getText());
}
List<Tree> children = (List<Tree>) selectedPropsTree.getChildren();
if (children!= null && !children.isEmpty())
{
List<String> properties = new ArrayList<String>(children.size());
for (Tree child : children) {
properties.add(child.getText());
}
return properties;
}
}
catch (RewriteCardinalityException re)
{
//Catch any error so it doesn't get thrown up the stack
rpeLogger().debug("Unhandled Error parsing the "+paramName+" clause: "+re);
}
catch (RecognitionException e)
{
rpeLogger().debug("Error parsing the \"+paramName+\" clause: "+param);
}
catch (InvalidQueryException iqe)
{
throw new InvalidSelectException(paramName, iqe.getQueryParam());
}
//Default to throw out an invalid query
throw new InvalidSelectException(paramName, param);
}
/**
* Takes the "where" parameter and turns it into a Java Object that can be used for querying
* @param whereParam String
* @return Query a parsed version of the where clause, represented in Java
*/
default Query getWhereClause(String whereParam) throws InvalidQueryException
{
if (whereParam == null) return QueryImpl.EMPTY;
try {
CommonTree whereTree = WhereCompiler.compileWhereClause(whereParam);
if (whereTree instanceof CommonErrorNode)
{
rpeLogger().debug("Error parsing the WHERE clause "+whereTree);
throw new InvalidQueryException(whereTree);
}
return new QueryImpl(whereTree);
} catch (RewriteCardinalityException re) { //Catch any error so it doesn't get thrown up the stack
rpeLogger().info("Unhandled Error parsing the WHERE clause: "+re);
} catch (RecognitionException e) {
whereParam += ", "+WhereCompiler.resolveMessage(e);
rpeLogger().info("Error parsing the WHERE clause: "+whereParam);
}
//Default to throw out an invalid query
throw new InvalidQueryException(whereParam);
}
/**
* Takes the Sort parameter as a String and parses it into a List of SortColumn objects.
* The format is a comma seperated list of "columnName sortDirection",
* e.g. "name DESC, age ASC". It is not case sensitive and the sort direction is optional
* It default to sort ASCENDING.
* @param sortParams - String passed in on the request
* @return - the sort columns or an empty list if the params were invalid.
*/
default List<SortColumn> getSort(String sortParams)
{
if (sortParams != null)
{
StringTokenizer st = new StringTokenizer(sortParams, ",");
List<SortColumn> sortedColumns = new ArrayList<SortColumn>(st.countTokens());
while (st.hasMoreTokens())
{
String token = st.nextToken();
StringTokenizer columnDesc = new StringTokenizer(token, " ");
if (columnDesc.countTokens() <= 2)
{
String columnName = columnDesc.nextToken();
String sortOrder = SortColumn.ASCENDING;
if (columnDesc.hasMoreTokens())
{
String sortDef = columnDesc.nextToken().toUpperCase();
if (SortColumn.ASCENDING.equals(sortDef) || SortColumn.DESCENDING.equals(sortDef))
{
sortOrder = sortDef;
}
else
{
rpeLogger().debug("Invalid sort order definition ("+sortDef+"). Valid values are "+SortColumn.ASCENDING+" or "+SortColumn.DESCENDING+".");
}
}
sortedColumns.add(new SortColumn(columnName, SortColumn.ASCENDING.equals(sortOrder)));
}
// filteredProperties.add();
}
// logger.debug("Filtering using the following properties: " + filteredProperties);
// BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
return sortedColumns;
}
return Collections.emptyList();
}
/**
* Find paging setings based on the request parameters.
*
* @param req
* @return Paging
*/
default Paging findPaging(WebScriptRequest req)
{
int skipped = Paging.DEFAULT_SKIP_COUNT;
int max = Paging.DEFAULT_MAX_ITEMS;
String skip = req.getParameter(PARAM_PAGING_SKIP);
String maxItems = req.getParameter(PARAM_PAGING_MAX);
try
{
if (skip != null) { skipped = Integer.parseInt(skip);}
if (maxItems != null) { max = Integer.parseInt(maxItems); }
if (max < 0 || skipped < 0)
{
throw new InvalidArgumentException("Negative values not supported.");
}
}
catch (NumberFormatException error)
{
if (rpeLogger().isDebugEnabled())
{
rpeLogger().debug("Invalid paging params skip: " + skip + ",maxItems:" + maxItems);
}
throw new InvalidArgumentException();
}
return Paging.valueOf(skipped, max);
}
/**
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
*
* Parses the parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
* filter param is set then a default BeanFilter is returned that will never
* filter fields (ie. Returns all bean properties).
*
* @param filterParams String
* @return BeanPropertyFilter - if no parameter then returns a new
* ReturnAllBeanProperties class
*/
default BeanPropertiesFilter getFilter(String filterParams)
{
return getFilter(filterParams, null);
}
/**
* Takes the web request and looks for a "relations" parameter Parses the
* parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertiesFilter it returned that uses the properties If no
* filter param is set then a default BeanFilter is returned that will never
* filter properties (ie. Returns all bean properties).
*
* @param filterParams String
* @return BeanPropertiesFilter - if no parameter then returns a new
* ReturnAllBeanProperties class
*/
default Map<String, BeanPropertiesFilter> getRelationFilter(String filterParams)
{
if (filterParams != null)
{
// Split by a comma when not in a bracket
String[] relations = filterParams.split(",(?![^()]*+\\))");
Map<String, BeanPropertiesFilter> filterMap = new HashMap<String, BeanPropertiesFilter>(relations.length);
for (String relation : relations)
{
int bracketLocation = relation.indexOf("(");
if (bracketLocation != -1)
{
// We have properties
String relationKey = relation.substring(0, bracketLocation);
String props = relation.substring(bracketLocation + 1, relation.length() - 1);
filterMap.put(relationKey, getFilter(props));
}
else
{
// no properties so just get the String
filterMap.put(relation, getFilter(null));
}
}
return filterMap;
}
return Collections.emptyMap();
}
/**
* Finds all request parameters that aren't already know about (eg. not paging or filter params)
* and returns them for use.
*
* @param req - the WebScriptRequest object
* @return the request parameters
*/
default Map<String, String[]> getRequestParameters(WebScriptRequest req)
{
if (req!= null)
{
String[] paramNames = req.getParameterNames();
if (paramNames!= null)
{
Map<String, String[]> requestParameteters = new HashMap<String, String[]>(paramNames.length);
for (int i = 0; i < paramNames.length; i++)
{
String paramName = paramNames[i];
if (!KNOWN_PARAMS.contains(paramName))
{
String[] vals = req.getParameterValues(paramName);
requestParameteters.put(paramName, vals);
}
}
return requestParameteters;
}
}
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,103 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.tools;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.JsonMappingException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
/*
* Reads information from the request
*
* @author Gethin James
*/
public interface RequestReader
{
/**
* Extracts the body contents from the request
*
* @param req the request
* @param jsonHelper Jackson Helper
* @param requiredType the type to return
* @return the Object in the required type
*/
default <T> T extractJsonContent(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
{
Reader reader;
try
{
reader = req.getContent().getReader();
return jsonHelper.construct(reader, requiredType);
}
catch (JsonMappingException e)
{
rrLogger().warn("Could not read content from HTTP request body.", e);
throw new InvalidArgumentException("Could not read content from HTTP request body.");
}
catch (IOException e)
{
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
}
}
/**
* Extracts the body contents from the request as a List, the JSON can be an array or just a single value without the [] symbols
*
* @param req the request
* @param jsonHelper Jackson Helper
* @param requiredType the type to return (without the List param)
* @return A List of "Object" as the required type
*/
default <T> List<T> extractJsonContentAsList(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
{
Reader reader;
try
{
reader = req.getContent().getReader();
return jsonHelper.constructList(reader, requiredType);
}
catch (IOException e)
{
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
}
}
default Log rrLogger() {
return LogFactory.getLog(this.getClass());
}
}

View File

@@ -0,0 +1,258 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.tools;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.core.exceptions.DefaultExceptionResolver;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.jacksonextensions.ExecutionResult;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.alfresco.rest.framework.resource.SerializablePagedCollection;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/*
* Writes to the response
*
* @author Gethin James
*/
public interface ResponseWriter
{
String UTF8 = "UTF-8";
ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(),UTF8, 0, null);
Cache CACHE_NEVER = new Cache(new Description.RequiredCache()
{
@Override
public boolean getNeverCache()
{
return true;
}
@Override
public boolean getIsPublic()
{
return false;
}
@Override
public boolean getMustRevalidate()
{
return true;
}
});
default Log resWriterLogger() {
return LogFactory.getLog(this.getClass());
}
/**
* Sets the response headers with any information we know about the content
* @param res WebScriptResponse
* @param contentInfo Content Information
*/
default void setContentInfoOnResponse(WebScriptResponse res, ContentInfo contentInfo)
{
if (contentInfo != null)
{
//Set content info on the response
res.setContentType(contentInfo.getMimeType());
res.setContentEncoding(contentInfo.getEncoding());
if (res instanceof WrappingWebScriptResponse)
{
WrappingWebScriptResponse wrappedRes = ((WrappingWebScriptResponse) res);
res = wrappedRes.getNext();
}
if (res instanceof WebScriptServletResponse)
{
WebScriptServletResponse servletResponse = (WebScriptServletResponse) res;
if (contentInfo.getLength() > 0)
{
if (contentInfo.getLength() > 0 && contentInfo.getLength() < Integer.MAX_VALUE)
{
servletResponse.getHttpServletResponse().setContentLength((int) contentInfo.getLength());
}
}
if (contentInfo.getLocale() != null)
{
servletResponse.getHttpServletResponse().setLocale(contentInfo.getLocale());
}
}
}
}
/**
* The response status must be set before the response is written by Jackson (which will by default close and commit the response).
* In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
*
* If you set content information via the contentInfo object and ALSO the headers then "headers" will win because they are
* set last.
*
* @param res
* @param status
* @param cache
* @param contentInfo
* @param headers
*/
default void setResponse(final WebScriptResponse res, int status, Cache cache, ContentInfo contentInfo, Map<String, List<String>> headers)
{
res.setStatus(status);
if (cache != null) res.setCache(cache);
setContentInfoOnResponse(res,contentInfo);
if (headers != null && !headers.isEmpty())
{
for (Map.Entry<String, List<String>> header:headers.entrySet())
{
for (int i=0; i < header.getValue().size(); i++) {
if (i==0)
{
//If its the first one then set the header overwriting.
res.setHeader(header.getKey(), header.getValue().get(i));
}
else
{
//If its not the first one than update the header
res.addHeader(header.getKey(), header.getValue().get(i));
}
}
}
}
}
/**
* Sets the response using the WithResponse object
* @param res
* @param withResponse
*/
default void setResponse(final WebScriptResponse res, WithResponse withResponse)
{
setResponse(res, withResponse.getStatus(), withResponse.getCache(), withResponse.getContentInfo(), withResponse.getHeaders());
}
/**
* Renders a JSON error response
* @param errorResponse The error
* @param res web script response
* @throws IOException
*/
default void renderErrorResponse(final ErrorResponse errorResponse, final WebScriptResponse res, final JacksonHelper jsonHelper) throws IOException {
String logId = "";
if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || resWriterLogger().isDebugEnabled())
{
logId = org.alfresco.util.GUID.generate();
resWriterLogger().error(logId+" : "+errorResponse.getStackTrace());
}
String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);
final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(),
errorResponse.getStatusCode(),
errorResponse.getBriefSummary(),
stackMessage,
logId,
errorResponse.getAdditionalState(),
DefaultExceptionResolver.ERROR_URL);
setContentInfoOnResponse(res, DEFAULT_JSON_CONTENT);
// Status must be set before the response is written by Jackson (which will by default close and commit the response).
// In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
res.setStatus(errorToWrite.getStatusCode());
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
{
@SuppressWarnings("unchecked")
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
throws JsonGenerationException, JsonMappingException, IOException
{
JSONObject obj = new JSONObject();
obj.put("error", errorToWrite);
objectMapper.writeValue(generator, obj);
}
});
}
/**
* Renders an exception to the output stream as Json.
* @param exception
* @param response
* @throws IOException
*/
default void renderException(final Exception exception, final WebScriptResponse response, final ApiAssistant assistant) throws IOException {
renderErrorResponse(assistant.resolveException(exception), response, assistant.getJsonHelper());
}
/**
* Renders the result of an execution.
*
* @param res WebScriptResponse
* @param toSerialize result of an execution
* @throws IOException
*/
default void renderJsonResponse(final WebScriptResponse res, final Object toSerialize, final JacksonHelper jsonHelper)
throws IOException
{
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
{
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
throws JsonGenerationException, JsonMappingException, IOException
{
objectMapper.writeValue(generator, toSerialize);
}
});
}
}

View File

@@ -53,6 +53,7 @@ import org.alfresco.rest.framework.resource.content.FileBinaryResource;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -80,7 +81,7 @@ import org.springframework.http.HttpMethod;
*/
// TODO for requests that pass in input streams e.g. binary content for workflow, this is going to need a way to re-read the input stream a la
// code in RepositoryContainer due to retrying transaction logic
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor
public abstract class AbstractResourceWebScript extends ApiWebScript implements HttpMethodSupport, ActionExecutor, ResponseWriter
{
private static Log logger = LogFactory.getLog(AbstractResourceWebScript.class);
@@ -146,18 +147,18 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
}
else
{
renderJsonResponse(res, toSerialize);
renderJsonResponse(res, toSerialize, assistant.getJsonHelper());
}
}
}
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
{
assistant.renderException(xception, res);
renderException(xception, res, assistant);
}
catch (RuntimeException runtimeException)
{
assistant.renderException(runtimeException, res);
renderException(runtimeException, res, assistant);
}
}
@@ -165,7 +166,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{
final String entityCollectionName = ResourceInspector.findEntityCollectionNameName(resource.getMetaData());
final ResourceOperation operation = resource.getMetaData().getOperation(getHttpMethod());
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
Object toReturn = transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
{
@@ -199,7 +200,7 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{
NodeBinaryResource nodeResource = (NodeBinaryResource) resource;
ContentInfo contentInfo = nodeResource.getContentInfo();
assistant.setContentInfoOnResponse(res, contentInfo);
setContentInfoOnResponse(res, contentInfo);
// if requested, set attachment
boolean attach = StringUtils.isNotEmpty(nodeResource.getAttachFileName());
Map<String, Object> model = getModelForCacheDirective(nodeResource.getCacheDirective());
@@ -217,70 +218,6 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
return null;
}
/**
* The response status must be set before the response is written by Jackson (which will by default close and commit the response).
* In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
*
* If you set content information via the contentInfo object and ALSO the headers then "headers" will win because they are
* set last.
*
* @param res
* @param status
* @param cache
* @param contentInfo
* @param headers
*/
public void setResponse(final WebScriptResponse res, int status, Cache cache, ContentInfo contentInfo, Map<String, List<String>> headers)
{
res.setStatus(status);
if (cache != null) res.setCache(cache);
assistant.setContentInfoOnResponse(res,contentInfo);
if (headers != null && !headers.isEmpty())
{
for (Map.Entry<String, List<String>> header:headers.entrySet())
{
for (int i=0; i < header.getValue().size(); i++) {
if (i==0)
{
//If its the first one then set the header overwriting.
res.setHeader(header.getKey(), header.getValue().get(i));
}
else
{
//If its not the first one than update the header
res.addHeader(header.getKey(), header.getValue().get(i));
}
}
}
}
}
protected void setResponse(final WebScriptResponse res, WithResponse withResponse)
{
setResponse(res, withResponse.getStatus(), withResponse.getCache(), withResponse.getContentInfo(), withResponse.getHeaders());
}
/**
* Renders the result of an execution.
*
* @param res WebScriptResponse
* @param toSerialize result of an execution
* @throws IOException
*/
protected void renderJsonResponse(final WebScriptResponse res, final Object toSerialize)
throws IOException
{
assistant.getJsonHelper().withWriter(res.getOutputStream(), new JacksonHelper.Writer()
{
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
throws JsonGenerationException, JsonMappingException, IOException
{
objectMapper.writeValue(generator, toSerialize);
}
});
}
public void setLocator(ResourceLocator locator)
{
this.locator = locator;

View File

@@ -38,6 +38,7 @@ import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResou
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
@@ -48,7 +49,7 @@ import org.springframework.http.HttpMethod;
*
* @author Gethin James
*/
public class ResourceWebScriptDelete extends AbstractResourceWebScript implements ParamsExtractor
public class ResourceWebScriptDelete extends AbstractResourceWebScript implements ParamsExtractor, RecognizedParamsExtractor
{
public ResourceWebScriptDelete()
@@ -63,7 +64,7 @@ public class ResourceWebScriptDelete extends AbstractResourceWebScript implement
{
String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
final Params.RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
final Params.RecognizedParams params = getRecognizedParams(req);
switch (resourceMeta.getType())
{
@@ -215,7 +216,7 @@ public class ResourceWebScriptDelete extends AbstractResourceWebScript implement
public Void execute(final ResourceWithMetadata resource, final Params params, final WebScriptResponse res, boolean isReadOnly)
{
final ResourceOperation operation = resource.getMetaData().getOperation(HttpMethod.DELETE);
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
final WithResponse callBack = new WithResponse(operation.getSuccessStatus(), DEFAULT_JSON_CONTENT,CACHE_NEVER);
transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Void>()
{

View File

@@ -40,6 +40,7 @@ import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -51,7 +52,7 @@ import org.springframework.http.HttpMethod;
*
* @author Gethin James
*/
public class ResourceWebScriptGet extends AbstractResourceWebScript implements ParamsExtractor
public class ResourceWebScriptGet extends AbstractResourceWebScript implements ParamsExtractor, RecognizedParamsExtractor
{
private static Log logger = LogFactory.getLog(ResourceWebScriptGet.class);
@@ -67,7 +68,7 @@ public class ResourceWebScriptGet extends AbstractResourceWebScript implements P
{
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
final RecognizedParams params = getRecognizedParams(req);
switch (resourceMeta.getType())
{

View File

@@ -67,6 +67,7 @@ import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryImpl;
import org.alfresco.rest.framework.resource.parameters.where.WhereCompiler;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.antlr.runtime.RecognitionException;
@@ -92,336 +93,10 @@ import org.springframework.http.HttpMethod;
public class ResourceWebScriptHelper
{
private static Log logger = LogFactory.getLog(ResourceWebScriptHelper.class);
public static final String PARAM_RELATIONS = "relations";
public static final String PARAM_FILTER_FIELDS = "fields";
@Deprecated
public static final String PARAM_FILTER_PROPERTIES = "properties";
public static final String PARAM_PAGING_SKIP = "skipCount";
public static final String PARAM_PAGING_MAX = "maxItems";
public static final String PARAM_ORDERBY = "orderBy";
public static final String PARAM_WHERE = "where";
public static final String PARAM_SELECT = "select";
public static final String PARAM_INCLUDE = "include";
public static final String PARAM_INCLUDE_SOURCE_ENTITY = "includeSource";
public static final List<String> KNOWN_PARAMS = Arrays.asList(
PARAM_RELATIONS, PARAM_FILTER_PROPERTIES, PARAM_FILTER_FIELDS,PARAM_PAGING_SKIP,PARAM_PAGING_MAX,
PARAM_ORDERBY, PARAM_WHERE, PARAM_SELECT, PARAM_INCLUDE_SOURCE_ENTITY);
private ResourceLocator locator;
private ActionExecutor executor;
/**
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
*
* Parses the parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
* filter param is set then a default BeanFilter is returned that will never
* filter fields (ie. Returns all bean properties).
*
* @param filterParams String
* @return BeanPropertyFilter - if no parameter then returns a new
* ReturnAllBeanProperties class
*/
public static BeanPropertiesFilter getFilter(String filterParams)
{
return getFilter(filterParams, null);
}
/**
* Takes the web request and looks for a "fields" parameter (otherwise deprecated "properties" parameter).
*
* Parses the parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertyFilter it returned that uses the bean properties. If no
* filter param is set then a default BeanFilter is returned that will never
* filter fields (ie. Returns all bean properties).
*
* If selectList is provided then it will take precedence (ie. be included) over the fields/properties filter
* for top-level entries (bean properties).
*
* For example, this will return entries from both select & properties, eg.
*
* select=abc,def&properties=id,name,ghi
*
* Note: it should be noted that API-generic "fields" clause does not currently work for sub-entries.
*
* Hence, even if the API-specific "select" clause allows selection of a sub-entries this cannot be used
* with "fields" filtering. For example, an API-specific method may implement and return "abc/blah", eg.
*
* select=abc/blah
*
* However the following will not return "abc/blah" if used with fields filtering, eg.
*
* select=abc/blah&fields=id,name,ghi
*
* If fields filtering is desired then it would require "abc" to be selected and returned as a whole, eg.
*
* select=abc&fields=id,name,ghi
*
* @param filterParams
* @param selectList
* @return
*/
public static BeanPropertiesFilter getFilter(String filterParams, List<String> selectList)
{
if (filterParams != null)
{
StringTokenizer st = new StringTokenizer(filterParams, ",");
Set<String> filteredProperties = new HashSet<String>(st.countTokens());
while (st.hasMoreTokens())
{
filteredProperties.add(st.nextToken());
}
// if supplied, the select takes precedence over the filter (fields/properties) for top-level bean properties
if (selectList != null)
{
for (String select : selectList)
{
String[] split = select.split("/");
filteredProperties.add(split[0]);
}
}
logger.debug("Filtering using the following properties: " + filteredProperties);
BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
return filter;
}
return BeanPropertiesFilter.ALLOW_ALL;
}
/**
* Takes the web request and looks for a "relations" parameter Parses the
* parameter and produces a list of bean properties to use as a filter A
* SimpleBeanPropertiesFilter it returned that uses the properties If no
* filter param is set then a default BeanFilter is returned that will never
* filter properties (ie. Returns all bean properties).
*
* @param filterParams String
* @return BeanPropertiesFilter - if no parameter then returns a new
* ReturnAllBeanProperties class
*/
public static Map<String, BeanPropertiesFilter> getRelationFilter(String filterParams)
{
if (filterParams != null)
{
// Split by a comma when not in a bracket
String[] relations = filterParams.split(",(?![^()]*+\\))");
Map<String, BeanPropertiesFilter> filterMap = new HashMap<String, BeanPropertiesFilter>(relations.length);
for (String relation : relations)
{
int bracketLocation = relation.indexOf("(");
if (bracketLocation != -1)
{
// We have properties
String relationKey = relation.substring(0, bracketLocation);
String props = relation.substring(bracketLocation + 1, relation.length() - 1);
filterMap.put(relationKey, getFilter(props));
}
else
{
// no properties so just get the String
filterMap.put(relation, getFilter(null));
}
}
return filterMap;
}
return Collections.emptyMap();
}
/**
* Takes the "select" parameter and turns it into a List<String> property names
* @param selectParam String
* @return bean property names potentially using JSON Pointer syntax
*/
@SuppressWarnings("unchecked")
@Deprecated
public static List<String> getSelectClause(String selectParam) throws InvalidArgumentException
{
return getClause(selectParam, "SELECT");
}
/**
* Takes the "include" parameter and turns it into a List<String> property names
* @param includeParam String
* @return bean property names potentially using JSON Pointer syntax
*/
@SuppressWarnings("unchecked")
public static List<String> getIncludeClause(String includeParam) throws InvalidArgumentException
{
return getClause(includeParam, "INCLUDE");
}
private static List<String> getClause(String param, String paramName)
{
if (param == null) return Collections.emptyList();
try {
CommonTree selectedPropsTree = WhereCompiler.compileSelectClause(param);
if (selectedPropsTree instanceof CommonErrorNode)
{
logger.debug("Error parsing the "+paramName+" clause "+selectedPropsTree);
throw new InvalidSelectException(paramName, selectedPropsTree);
}
if (selectedPropsTree.getChildCount() == 0 && !selectedPropsTree.getText().isEmpty())
{
return Arrays.asList(selectedPropsTree.getText());
}
List<Tree> children = (List<Tree>) selectedPropsTree.getChildren();
if (children!= null && !children.isEmpty())
{
List<String> properties = new ArrayList<String>(children.size());
for (Tree child : children) {
properties.add(child.getText());
}
return properties;
}
}
catch (RewriteCardinalityException re)
{
//Catch any error so it doesn't get thrown up the stack
logger.debug("Unhandled Error parsing the "+paramName+" clause: "+re);
}
catch (RecognitionException e)
{
logger.debug("Error parsing the \"+paramName+\" clause: "+param);
}
catch (InvalidQueryException iqe)
{
throw new InvalidSelectException(paramName, iqe.getQueryParam());
}
//Default to throw out an invalid query
throw new InvalidSelectException(paramName, param);
}
/**
* Takes the "where" parameter and turns it into a Java Object that can be used for querying
* @param whereParam String
* @return Query a parsed version of the where clause, represented in Java
*/
public static Query getWhereClause(String whereParam) throws InvalidQueryException
{
if (whereParam == null) return QueryImpl.EMPTY;
try {
CommonTree whereTree = WhereCompiler.compileWhereClause(whereParam);
if (whereTree instanceof CommonErrorNode)
{
logger.debug("Error parsing the WHERE clause "+whereTree);
throw new InvalidQueryException(whereTree);
}
return new QueryImpl(whereTree);
} catch (RewriteCardinalityException re) { //Catch any error so it doesn't get thrown up the stack
logger.info("Unhandled Error parsing the WHERE clause: "+re);
} catch (RecognitionException e) {
whereParam += ", "+WhereCompiler.resolveMessage(e);
logger.info("Error parsing the WHERE clause: "+whereParam);
}
//Default to throw out an invalid query
throw new InvalidQueryException(whereParam);
}
/**
* Takes the Sort parameter as a String and parses it into a List of SortColumn objects.
* The format is a comma seperated list of "columnName sortDirection",
* e.g. "name DESC, age ASC". It is not case sensitive and the sort direction is optional
* It default to sort ASCENDING.
* @param sortParams - String passed in on the request
* @return - the sort columns or an empty list if the params were invalid.
*/
public static List<SortColumn> getSort(String sortParams)
{
if (sortParams != null)
{
StringTokenizer st = new StringTokenizer(sortParams, ",");
List<SortColumn> sortedColumns = new ArrayList<SortColumn>(st.countTokens());
while (st.hasMoreTokens())
{
String token = st.nextToken();
StringTokenizer columnDesc = new StringTokenizer(token, " ");
if (columnDesc.countTokens() <= 2)
{
String columnName = columnDesc.nextToken();
String sortOrder = SortColumn.ASCENDING;
if (columnDesc.hasMoreTokens())
{
String sortDef = columnDesc.nextToken().toUpperCase();
if (SortColumn.ASCENDING.equals(sortDef) || SortColumn.DESCENDING.equals(sortDef))
{
sortOrder = sortDef;
}
else
{
logger.debug("Invalid sort order definition ("+sortDef+"). Valid values are "+SortColumn.ASCENDING+" or "+SortColumn.DESCENDING+".");
}
}
sortedColumns.add(new SortColumn(columnName, SortColumn.ASCENDING.equals(sortOrder)));
}
// filteredProperties.add();
}
// logger.debug("Filtering using the following properties: " + filteredProperties);
// BeanPropertiesFilter filter = new BeanPropertiesFilter(filteredProperties);
return sortedColumns;
}
return Collections.emptyList();
}
/**
* Extracts the body contents from the request
*
* @param req the request
* @param jsonHelper Jackson Helper
* @param requiredType the type to return
* @return the Object in the required type
*/
public static <T> T extractJsonContent(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
{
Reader reader;
try
{
reader = req.getContent().getReader();
return jsonHelper.construct(reader, requiredType);
}
catch (JsonMappingException e)
{
logger.warn("Could not read content from HTTP request body.", e);
throw new InvalidArgumentException("Could not read content from HTTP request body.");
}
catch (IOException e)
{
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
}
}
/**
* Extracts the body contents from the request as a List, the JSON can be an array or just a single value without the [] symbols
*
* @param req the request
* @param jsonHelper Jackson Helper
* @param requiredType the type to return (without the List param)
* @return A List of "Object" as the required type
*/
public static <T> List<T> extractJsonContentAsList(WebScriptRequest req, JacksonHelper jsonHelper, Class<T> requiredType)
{
Reader reader;
try
{
reader = req.getContent().getReader();
return jsonHelper.constructList(reader, requiredType);
}
catch (IOException e)
{
throw new ApiException("Could not read content from HTTP request body.", e.getCause());
}
}
/**
* Set the id of theObj to the uniqueId. Attempts to find a set method and
* invoke it. If it fails it just swallows the exceptions and doesn't throw
@@ -471,41 +146,6 @@ public class ResourceWebScriptHelper
}
}
// /**
// * Renders the response result
// *
// * @param response
// * @param result
// */
// public static void renderResponseDep(Map<String, Object> response, Object result)
// {
//
// if (result == null) { return; }
//
// if (result instanceof Collection)
// {
// response.put("list", result);
// }
// else if (result instanceof CollectionWithPagingInfo)
// {
// CollectionWithPagingInfo<?> col = (CollectionWithPagingInfo<?>) result;
// if (col.getCollection() !=null && !col.getCollection().isEmpty())
// {
// response.put("list", col);
// }
// }
// else if (result instanceof Pair<?,?>)
// {
// Pair<?,?> aPair = (Pair<?, ?>) result;
// response.put("entry", aPair.getFirst());
// response.put("relations", aPair.getSecond());
// }
// else
// {
// response.put("entry", result);
// }
// }
/**
* Looks at the object passed in and recursively expands any @EmbeddedEntityResource annotations or related relationship.
* {@link org.alfresco.rest.framework.resource.EmbeddedEntityResource EmbeddedEntityResource} is expanded by calling the ReadById method for this entity.
@@ -677,7 +317,7 @@ public class ResourceWebScriptHelper
paramFilter = filters.get(resourceKey);
}
final Params executionParams = Params.valueOf(paramFilter, uniqueEntityId, params.getRequest());
final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
final WithResponse callBack = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT,ResponseWriter.CACHE_NEVER);
//Read only because this only occurs for GET requests
Object result = executor.executeAction(resource, executionParams, callBack);
return processAdditionsToTheResponse(null, api, null, executionParams, result);
@@ -705,118 +345,6 @@ public class ResourceWebScriptHelper
return null; //default
}
/**
* Finds all request parameters that aren't already known about (eg. not paging or filter params)
* and returns them for use.
*
* @param req - the WebScriptRequest object
* @return the request parameters
*/
public static Map<String, String[]> getRequestParameters(WebScriptRequest req)
{
if (req!= null)
{
String[] paramNames = req.getParameterNames();
if (paramNames!= null)
{
Map<String, String[]> requestParameteters = new HashMap<String, String[]>(paramNames.length);
for (int i = 0; i < paramNames.length; i++)
{
String paramName = paramNames[i];
if (!KNOWN_PARAMS.contains(paramName))
{
String[] vals = req.getParameterValues(paramName);
requestParameteters.put(paramName, vals);
}
}
return requestParameteters;
}
}
return Collections.emptyMap();
}
/**
* Finds the formal set of params that any rest service could potentially have passed in as request params
* @param req WebScriptRequest
* @return RecognizedParams a POJO containing the params for use with the Params objects
*/
public static RecognizedParams getRecognizedParams(WebScriptRequest req)
{
Paging paging = findPaging(req);
List<SortColumn> sorting = getSort(req.getParameter(ResourceWebScriptHelper.PARAM_ORDERBY));
Map<String, BeanPropertiesFilter> relationFilter = getRelationFilter(req.getParameter(ResourceWebScriptHelper.PARAM_RELATIONS));
Query whereQuery = getWhereClause(req.getParameter(ResourceWebScriptHelper.PARAM_WHERE));
Map<String, String[]> requestParams = getRequestParameters(req);
boolean includeSource = Boolean.valueOf(req.getParameter(ResourceWebScriptHelper.PARAM_INCLUDE_SOURCE_ENTITY));
List<String> includedFields = getIncludeClause(req.getParameter(ResourceWebScriptHelper.PARAM_INCLUDE));
List<String> selectFields = getSelectClause(req.getParameter(ResourceWebScriptHelper.PARAM_SELECT));
String fields = req.getParameter(ResourceWebScriptHelper.PARAM_FILTER_FIELDS);
String properties = req.getParameter(ResourceWebScriptHelper.PARAM_FILTER_PROPERTIES);
if ((fields != null) && (properties != null))
{
if (logger.isWarnEnabled())
{
logger.warn("Taking 'fields' param [" + fields + "] and ignoring deprecated 'properties' param [" + properties + "]");
}
}
BeanPropertiesFilter filter = getFilter((fields != null ? fields : properties), includedFields);
return new RecognizedParams(requestParams, paging, filter, relationFilter, includedFields, selectFields, whereQuery, sorting, includeSource);
}
/**
* Find paging setings based on the request parameters.
*
* @param req
* @return Paging
*/
public static Paging findPaging(WebScriptRequest req)
{
int skipped = Paging.DEFAULT_SKIP_COUNT;
int max = Paging.DEFAULT_MAX_ITEMS;
String skip = req.getParameter(PARAM_PAGING_SKIP);
String maxItems = req.getParameter(PARAM_PAGING_MAX);
try
{
if (skip != null) { skipped = Integer.parseInt(skip);}
if (maxItems != null) { max = Integer.parseInt(maxItems); }
if (skipped < 0)
{
throw new InvalidArgumentException("Negative values not supported for skipCount.");
}
if (max < 1)
{
throw new InvalidArgumentException("Only positive values supported for maxItems.");
}
}
catch (NumberFormatException error)
{
String errorMsg = "Invalid paging parameters skipCount: " + skip + ", maxItems:" + maxItems;
if (logger.isDebugEnabled())
{
logger.debug(errorMsg);
}
if (skip == null)
{
errorMsg = "Invalid paging parameter maxItems:" + maxItems;
}
if (maxItems == null)
{
errorMsg = "Invalid paging parameter skipCount:" + skip;
}
throw new InvalidArgumentException(errorMsg);
}
return Paging.valueOf(skipped, max);
}
public void setLocator(ResourceLocator locator)
{
this.locator = locator;

View File

@@ -45,6 +45,8 @@ import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResou
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.tools.RequestReader;
import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptRequestImpl;
@@ -56,7 +58,8 @@ import org.springframework.http.HttpMethod;
*
* @author Gethin James
*/
public class ResourceWebScriptPost extends AbstractResourceWebScript implements ParamsExtractor
public class ResourceWebScriptPost extends AbstractResourceWebScript implements ParamsExtractor,
RecognizedParamsExtractor, RequestReader
{
public ResourceWebScriptPost()
@@ -69,7 +72,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
@Override
public Params extractParams(ResourceMetadata resourceMeta, WebScriptRequest req)
{
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
final RecognizedParams params = getRecognizedParams(req);
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
final ResourceOperation operation = resourceMeta.getOperation(HttpMethod.POST);
@@ -168,7 +171,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
Object jsonContent = null;
if (objType != null)
{
jsonContent = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), objType);
jsonContent = extractJsonContent(req, assistant.getJsonHelper(), objType);
}
if (isTypeOperation)
@@ -203,11 +206,11 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
if (isTypeOperation)
{
// Operations don't support a List as json body
return ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), objType);
return extractJsonContent(req, assistant.getJsonHelper(), objType);
}
else
{
return ResourceWebScriptHelper.extractJsonContentAsList(req, assistant.getJsonHelper(), objType);
return extractJsonContentAsList(req, assistant.getJsonHelper(), objType);
}
}

View File

@@ -44,6 +44,8 @@ import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.tools.RequestReader;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -57,7 +59,8 @@ import org.springframework.http.MediaType;
*
* @author Gethin James
*/
public class ResourceWebScriptPut extends AbstractResourceWebScript implements ParamsExtractor
public class ResourceWebScriptPut extends AbstractResourceWebScript implements ParamsExtractor,
RecognizedParamsExtractor, RequestReader
{
private static Log logger = LogFactory.getLog(ResourceWebScriptPut.class);
@@ -75,7 +78,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
final String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
final String entityId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.ENTITY_ID);
final RecognizedParams params = ResourceWebScriptHelper.getRecognizedParams(req);
final RecognizedParams params = getRecognizedParams(req);
final ResourceOperation operation = resourceMeta.getOperation(HttpMethod.PUT);
switch (resourceMeta.getType())
@@ -87,7 +90,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
} else
{
Object putEnt = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
Object putEnt = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
return Params.valueOf(entityId,params,putEnt, req);
}
case RELATIONSHIP:
@@ -96,7 +99,7 @@ public class ResourceWebScriptPut extends AbstractResourceWebScript implements P
throw new UnsupportedResourceOperationException("PUT is executed against the instance URL");
} else
{
Object putRel = ResourceWebScriptHelper.extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
Object putRel = extractJsonContent(req, assistant.getJsonHelper(), resourceMeta.getObjectType(operation));
ResourceWebScriptHelper.setUniqueId(putRel,relationshipId);
return Params.valueOf(entityId, params, putRel, req);
}

View File

@@ -39,6 +39,7 @@ import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
@@ -81,7 +82,7 @@ public abstract class AbstractContextTest
static Params NOT_USED = Params.valueOf("notUsed", null, mock(WebScriptRequest.class));
static final Params.RecognizedParams NULL_PARAMS = new Params.RecognizedParams(null, null, null, null, null, null, null, null, false);
static final WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT,ApiAssistant.CACHE_NEVER);
static final WithResponse callBack = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT,ResponseWriter.CACHE_NEVER);
static Api api = Api.valueOf("alfrescomock", "private", "1");
@SuppressWarnings("unchecked")

View File

@@ -25,6 +25,7 @@
*/
package org.alfresco.rest.framework.tests.core;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractorTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@@ -37,7 +38,7 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({ InspectorTests.class, JsonJacksonTests.class, ParamsExtractorTests.class,
ResourceLocatorTests.class, ResourceWebScriptHelperTests.class, SerializeTests.class,
WhereTests.class, ExecutionTests.class, WithResponseTest.class,
ExceptionResolverTests.class })
ExceptionResolverTests.class, RecognizedParamsExtractorTest.class})
public class AllRestFrameworkTest
{

View File

@@ -51,6 +51,7 @@ import org.alfresco.rest.framework.tests.api.mocks.Grass;
import org.alfresco.rest.framework.tests.api.mocks.Sheep;
import org.alfresco.rest.framework.tests.api.mocks3.FlockEntityResource;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.junit.Test;
@@ -80,7 +81,7 @@ import java.util.Map;
* Tests the execution of resources
*/
public class ExecutionTests extends AbstractContextTest
public class ExecutionTests extends AbstractContextTest implements ResponseWriter
{
static final Api api3 = Api.valueOf("alfrescomock", "private", "3");
@@ -99,7 +100,7 @@ public class ExecutionTests extends AbstractContextTest
entityResource = locator.locateEntityResource(api,"cow", HttpMethod.GET);
result = executor.execute(entityResource, Params.valueOf((String)null, null, mock(WebScriptRequest.class)), response, true);
assertNotNull(result);
verify(response, times(1)).setCache((Cache) ApiAssistant.CACHE_NEVER);
verify(response, times(1)).setCache((Cache) ResponseWriter.CACHE_NEVER);
response = mock(WebScriptResponse.class);
result = executor.execute(entityResource, Params.valueOf("543", null, mock(WebScriptRequest.class)), response, true);
@@ -305,7 +306,7 @@ public class ExecutionTests extends AbstractContextTest
ErrorResponse defaultError = new DefaultExceptionResolver().resolveException(new NullPointerException());
ByteArrayOutputStream out = new ByteArrayOutputStream();
apiAssistant.renderErrorResponse(defaultError, mockResponse(out));
renderErrorResponse(defaultError, mockResponse(out), apiAssistant.getJsonHelper());
String errorMessage = out.toString();
// System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.ApiDefault\""));
@@ -316,7 +317,7 @@ public class ExecutionTests extends AbstractContextTest
ErrorResponse anError = simpleMappingExceptionResolver.resolveException(new ApiException("nothing"));
out = new ByteArrayOutputStream();
apiAssistant.renderErrorResponse(anError, mockResponse(out));
renderErrorResponse(anError, mockResponse(out),apiAssistant.getJsonHelper());
errorMessage = out.toString();
// System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"nothing\""));
@@ -326,7 +327,7 @@ public class ExecutionTests extends AbstractContextTest
anError = simpleMappingExceptionResolver.resolveException(new EntityNotFoundException("2"));
out = new ByteArrayOutputStream();
apiAssistant.renderErrorResponse(anError, mockResponse(out));
renderErrorResponse(anError, mockResponse(out),apiAssistant.getJsonHelper());
errorMessage = out.toString();
System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.EntityNotFound\""));

View File

@@ -77,6 +77,7 @@ import org.alfresco.rest.framework.tests.api.mocks3.SheepBlackSheepResourceIsNoM
import org.alfresco.rest.framework.tests.api.mocks3.SheepEntityResourceWithDeletedMethods;
import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.util.Pair;
@@ -466,7 +467,7 @@ public class InspectorTests
OperationResourceMetaData operationResourceMetaData = (OperationResourceMetaData) resourceMetadata;
Method actionMethod = operationResourceMetaData.getOperationMethod();
String result = null;
final WithResponse wr = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
final WithResponse wr = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
switch (resourceMetadata.getUniqueId())
{

View File

@@ -49,6 +49,7 @@ import org.alfresco.rest.framework.tests.api.mocks.Farmer;
import org.alfresco.rest.framework.tests.api.mocks.Goat;
import org.alfresco.rest.framework.tests.api.mocks.Grass;
import org.alfresco.rest.framework.tests.api.mocks.UniqueIdMethodButNoSetter;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
@@ -63,329 +64,6 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
public class ResourceWebScriptHelperTests
{
@Test
public void getFilterTest()
{
BeanPropertiesFilter theFilter = ResourceWebScriptHelper.getFilter(null);
assertNotNull(theFilter);
assertTrue("Null passed in so must return the default BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(theFilter.getClass()));
theFilter = ResourceWebScriptHelper.getFilter("bob");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
theFilter = ResourceWebScriptHelper.getFilter("50,fred,b.z");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
theFilter = ResourceWebScriptHelper.getFilter("50,fred,");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
}
@Test
public void getSortingTest()
{
List<SortColumn> theSort = ResourceWebScriptHelper.getSort(null);
assertNotNull(theSort);
assertTrue("Null passed in so empty sort list should be returned.", theSort.isEmpty());
theSort = ResourceWebScriptHelper.getSort("name ASC");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
theSort = ResourceWebScriptHelper.getSort("name ");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
theSort = ResourceWebScriptHelper.getSort("name DESC");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
theSort = ResourceWebScriptHelper.getSort("name desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
theSort = ResourceWebScriptHelper.getSort("name,age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = ResourceWebScriptHelper.getSort(" name, age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = ResourceWebScriptHelper.getSort("name DESC, age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = ResourceWebScriptHelper.getSort("age Desc, name Asc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("age", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
assertEquals("name", theSort.get(1).column);
assertTrue(theSort.get(1).asc);
theSort = ResourceWebScriptHelper.getSort("name des"); //invalid, should be desc
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc); //Defaults to ascending because the sort order was invalid
theSort = ResourceWebScriptHelper.getSort("name asc,"); //invalid, should be desc
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
}
@Test
public void getIncludeClauseTest()
{
getClauseTest("include");
}
@Test
public void getSelectClauseTest()
{
getClauseTest("select");
}
// at the moment select and include are parsed the same way, hence common/shared test
private void getClauseTest(String paramName)
{
List<String> theClause = getClause(paramName, null);
assertNotNull(theClause);
assertFalse("Null passed in so nothing in the "+paramName, theClause.size() > 0);
try
{
theClause = getClause(paramName, ",,,");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "(,,,");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "(,,,)");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "x/,z");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "/x'n,/z");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "/foo/0");
fail("Should throw an InvalidSelectException. Legal identifiers must start with a letter not zero");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "/");
fail("Should throw an InvalidSelectException. No identifier specified.");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getClause(paramName, "path, isLink");
fail("Should throw an InvalidSelectException. No identifier specified.");
}
catch (InvalidSelectException error)
{
//this is correct
}
theClause = getClause(paramName, "king/kong");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("king/kong",theClause.get(0));
theClause = getClause(paramName, "x,y");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("x",theClause.get(0));
assertEquals("y",theClause.get(1));
theClause = getClause(paramName, "x,/z");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("x",theClause.get(0));
assertEquals("/z",theClause.get(1));
theClause = getClause(paramName, "/b");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/b",theClause.get(0));
theClause = getClause(paramName, "/be,/he");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("/be",theClause.get(0));
assertEquals("/he",theClause.get(1));
theClause = getClause(paramName, "/king/kong");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/king/kong",theClause.get(0));
theClause = getClause(paramName, "/name,/person/age");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("/name",theClause.get(0));
assertEquals("/person/age",theClause.get(1));
theClause = getClause(paramName, "/foo");
assertTrue("has a valid select",theClause.size() == 1);
assertEquals("/foo",theClause.get(0));
theClause = getClause(paramName, "/foo/anArray/x");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/foo/anArray/x",theClause.get(0));
theClause = getClause(paramName, "/foo/anArray/x,/person/age,/eggs/bacon/sausage,/p");
assertTrue("has a valid "+paramName, theClause.size() == 4);
assertEquals("/foo/anArray/x",theClause.get(0));
assertEquals("/person/age",theClause.get(1));
assertEquals("/eggs/bacon/sausage",theClause.get(2));
assertEquals("/p",theClause.get(3));
theClause = getClause(paramName, "/foo/_bar ");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/foo/_bar",theClause.get(0));
}
private List<String> getClause(String paramName, String paramValue)
{
if (paramName.equalsIgnoreCase("include"))
{
return ResourceWebScriptHelper.getIncludeClause(paramValue);
}
else if (paramName.equalsIgnoreCase("select"))
{
return ResourceWebScriptHelper.getSelectClause(paramValue);
}
fail("Unexpected clause: "+paramName);
return null;
}
@Test
public void getRelationFilterTest()
{
Map<String, BeanPropertiesFilter> theFilter = ResourceWebScriptHelper.getRelationFilter(null);
assertNotNull(theFilter);
assertTrue("Null passed in so nothing to filter.",theFilter.isEmpty());
theFilter = ResourceWebScriptHelper.getRelationFilter("bob");
assertNotNull(theFilter);
assertTrue("Must be a single relationship", theFilter.size() == 1);
assertTrue("Must be a single relationship called bob", theFilter.containsKey("bob"));
BeanPropertiesFilter aFilter = theFilter.get("bob");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
theFilter = ResourceWebScriptHelper.getRelationFilter("bob,hope");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
assertTrue("Must have hope.", theFilter.containsKey("hope"));
aFilter = theFilter.get("hope");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
theFilter = ResourceWebScriptHelper.getRelationFilter("bob(name),hope");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
assertTrue("Must have bob.", theFilter.containsKey("bob"));
aFilter = theFilter.get("bob");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
theFilter = ResourceWebScriptHelper.getRelationFilter("bob,hope(age,name)");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
aFilter = theFilter.get("bob");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
aFilter = theFilter.get("hope");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
theFilter = ResourceWebScriptHelper.getRelationFilter("bob(name,age),nohope,hope(height,width)");
assertNotNull(theFilter);
assertTrue("Must be a three relationships", theFilter.size() == 3);
aFilter = theFilter.get("bob");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
aFilter = theFilter.get("nohope");
assertTrue("No bean properties specified so need a ReturnAllBeanProperties class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
aFilter = theFilter.get("hope");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
}
@Test
public void setUniqueIdTest()
{
@@ -406,161 +84,4 @@ public class ResourceWebScriptHelperTests
assertNotNull("There should not be an error, errors should be swallowed up.",invalidbj);
}
@Test
public void findPagingTest()
{
WebScriptRequest request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("34");
when(request.getParameter("maxItems")).thenReturn("50");
Paging pagin = ResourceWebScriptHelper.findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == 34);
assertTrue(pagin.getMaxItems() == 50);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn(null);
when(request.getParameter("maxItems")).thenReturn(null);
pagin = ResourceWebScriptHelper.findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
assertTrue(pagin.getMaxItems() == Paging.DEFAULT_MAX_ITEMS);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("55");
pagin = ResourceWebScriptHelper.findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == 55);
assertTrue(pagin.getMaxItems() == Paging.DEFAULT_MAX_ITEMS);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn(null);
when(request.getParameter("maxItems")).thenReturn("45");
pagin = ResourceWebScriptHelper.findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getMaxItems() == 45);
assertTrue(pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("apple");
when(request.getParameter("maxItems")).thenReturn("pear");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("0");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
//Test Case cloud-2198
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("a");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("s");
when(request.getParameter("maxItems")).thenReturn("5");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("-2");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("-3");
when(request.getParameter("maxItems")).thenReturn("5");
try
{
pagin = ResourceWebScriptHelper.findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("maxItems")).thenReturn("5");
pagin = ResourceWebScriptHelper.findPaging(request);
assertNotNull(pagin);
assertTrue("skip count defaults to 0", pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
//End of Test Case cloud-2198
}
@Test
public void paramsTest()
{
Map<String,List<String>> mockParams = new HashMap<String,List<String>>();
mockParams.put("age", Arrays.asList("23","45"));
mockParams.put("name", Arrays.asList("fred"));
WebScriptRequest request = mockRequest(mockParams);
Map<String, String[]> params = ResourceWebScriptHelper.getRequestParameters(request);
assertNotNull(params);
Params paramObj = ParamsExtender.valueOf(params);
assertNotNull(paramObj);
String aValue = paramObj.getParameter("age");
assertEquals("23", aValue);
aValue = paramObj.getParameter("name");
assertEquals("fred", aValue);
}
private WebScriptRequest mockRequest(final Map<String,List<String>> params)
{
final String[] paramNames = params.keySet().toArray(new String[]{});
WebScriptRequest request = mock(WebScriptRequest.class);
when(request.getParameterNames()).thenReturn(paramNames);
when(request.getParameterValues(anyString())).thenAnswer(new Answer<String[]>() {
@Override
public String[] answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return params.get((String) args[0]).toArray(new String[]{});
}
});
return request;
}
}

View File

@@ -57,6 +57,7 @@ import org.alfresco.rest.framework.tests.api.mocks.Grass;
import org.alfresco.rest.framework.tests.api.mocks.Sheep;
import org.alfresco.rest.framework.tests.api.mocks3.Flock;
import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -91,7 +92,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class SerializeTests extends AbstractContextTest
public class SerializeTests extends AbstractContextTest implements RecognizedParamsExtractor
{
@Test
@@ -197,7 +198,7 @@ public class SerializeTests extends AbstractContextTest
public void testExpandRelations() throws IOException
{
assertNotNull(helper);
Map<String, BeanPropertiesFilter> rFilter = ResourceWebScriptHelper.getRelationFilter("blacksheep,baaahh");
Map<String, BeanPropertiesFilter> rFilter = getRelationFilter("blacksheep,baaahh");
ExecutionResult res = (ExecutionResult) helper.processAdditionsToTheResponse(mock(WebScriptResponse.class), api,"sheep",ParamsExtender.valueOf(rFilter,"1"),new Farmer("180"));
assertNotNull(res);
String out = writeResponse(res);
@@ -239,7 +240,7 @@ public class SerializeTests extends AbstractContextTest
public void testExpandRecursiveRelations() throws IOException
{
ExecutionResult exec1 = new ExecutionResult(new Farmer("180"),null);
ExecutionResult exec2 = new ExecutionResult(new Farmer("456"),ResourceWebScriptHelper.getFilter("age"));
ExecutionResult exec2 = new ExecutionResult(new Farmer("456"),getFilter("age"));
CollectionWithPagingInfo<ExecutionResult> coll = CollectionWithPagingInfo.asPaged(null, Arrays.asList(exec1, exec2));
ExecutionResult execResult = new ExecutionResult(new Sheep("ssheep"),null);
Map<String,Object> related = new HashMap<String,Object>();
@@ -436,12 +437,12 @@ public class SerializeTests extends AbstractContextTest
public void testFilter() throws IOException, JSONException
{
assertNotNull(helper);
BeanPropertiesFilter theFilter = ResourceWebScriptHelper.getFilter("age");
BeanPropertiesFilter theFilter = getFilter("age");
Object res = new ExecutionResult(new Sheep("bob"),theFilter);
String out = writeResponse(res);
assertTrue("Filter must only return the age.", StringUtils.contains(out, "{\"age\":3}"));
theFilter = ResourceWebScriptHelper.getFilter("age,name");
theFilter = getFilter("age,name");
res = new ExecutionResult(new Sheep("bob"),theFilter);
out = writeResponse(res);
JSONObject jsonRsp = new JSONObject(new JSONTokener(out));
@@ -452,8 +453,8 @@ public class SerializeTests extends AbstractContextTest
assertTrue("The age should be 3", entry.getInt("age") == 3);
// unit test filter with "include" taking precendence over "fields" filter
List<String> theInclude = ResourceWebScriptHelper.getIncludeClause("name");
theFilter = ResourceWebScriptHelper.getFilter("age", theInclude);
List<String> theInclude = getIncludeClause("name");
theFilter = getFilter("age", theInclude);
res = new ExecutionResult(new Sheep("bob"),theFilter);
out = writeResponse(res);
jsonRsp = new JSONObject(new JSONTokener(out));
@@ -464,7 +465,7 @@ public class SerializeTests extends AbstractContextTest
assertTrue("The age should be 3", entry.getInt("age") == 3);
Api v3 = Api.valueOf(api.getName(), api.getScope().toString(), "3");
Map<String, BeanPropertiesFilter> relFiler = ResourceWebScriptHelper.getRelationFilter("herd");
Map<String, BeanPropertiesFilter> relFiler = getRelationFilter("herd");
res = helper.processAdditionsToTheResponse(mock(WebScriptResponse.class), v3,"goat",ParamsExtender.valueOf(relFiler, "notUsed"),new SlimGoat());
out = writeResponse(res);
jsonRsp = new JSONObject(new JSONTokener(out));
@@ -476,7 +477,7 @@ public class SerializeTests extends AbstractContextTest
assertEquals("The name should be 'bigun'", "bigun", entry.getString("name"));
assertTrue("The quantity should be 56", entry.getInt("quantity") == 56);
relFiler = ResourceWebScriptHelper.getRelationFilter("herd(name)");
relFiler = getRelationFilter("herd(name)");
res = helper.processAdditionsToTheResponse(mock(WebScriptResponse.class), v3,"goat",ParamsExtender.valueOf(relFiler, "notUsed"),new SlimGoat());
out = writeResponse(res);
assertTrue("Must return only the herd name.", StringUtils.contains(out, "{\"name\":\"bigun\"}"));

View File

@@ -37,16 +37,17 @@ import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryExcepti
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper.WalkerCallbackAdapter;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.antlr.runtime.tree.CommonTree;
import org.junit.Test;
public class WhereTests {
public class WhereTests implements RecognizedParamsExtractor
{
@Test
public void basicTest() throws IOException {
Query theQuery = ResourceWebScriptHelper.getWhereClause(" ( fred > g ) ");
Query theQuery = getWhereClause(" ( fred > g ) ");
CommonTree ast = theQuery.getTree();
//check AST structure
assertEquals(WhereClauseParser.GREATERTHAN, ast.getType());
@@ -57,13 +58,13 @@ public class WhereTests {
@Test
public void existClauseTest()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause(null);
Query theQuery = getWhereClause(null);
assertNotNull(theQuery);
assertTrue("Null passed in so nothing to theQuery.", theQuery.getTree() == null);
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("fred");
theQuery = getWhereClause("fred");
fail("Should throw an InvalidQueryException");
}
catch (InvalidQueryException error)
@@ -73,7 +74,7 @@ public class WhereTests {
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("(noClosingBracket");
theQuery = getWhereClause("(noClosingBracket");
fail("Should throw an InvalidQueryException");
}
catch (InvalidQueryException error)
@@ -83,7 +84,7 @@ public class WhereTests {
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("noOpeningBracket)");
theQuery = getWhereClause("noOpeningBracket)");
fail("Should throw an InvalidQueryException");
}
catch (InvalidQueryException error)
@@ -94,7 +95,7 @@ public class WhereTests {
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(target.file))");
theQuery = getWhereClause("(EXISTS(target.file))");
fail("Should throw an InvalidQueryException");
}
catch (InvalidQueryException error)
@@ -102,34 +103,34 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("(exists(/target/file))");
theQuery = getWhereClause("(exists(/target/file))");
assertExistsPropertyEquals("/target/file", theQuery, false);
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(b))");
theQuery = getWhereClause("(EXISTS(b))");
assertExistsPropertyEquals("b", theQuery, false);
theQuery = ResourceWebScriptHelper.getWhereClause(" ( EXISTS ( whitespace ) ) ");
theQuery = getWhereClause(" ( EXISTS ( whitespace ) ) ");
assertExistsPropertyEquals("whitespace", theQuery, false);
theQuery = ResourceWebScriptHelper.getWhereClause("(exists ( folder ))");
theQuery = getWhereClause("(exists ( folder ))");
assertExistsPropertyEquals("folder", theQuery, false);
theQuery = ResourceWebScriptHelper.getWhereClause("(NOT EXISTS(b))");
theQuery = getWhereClause("(NOT EXISTS(b))");
assertExistsPropertyEquals("b", theQuery, true);
theQuery = ResourceWebScriptHelper.getWhereClause(" (NOT EXISTS(b))");
theQuery = getWhereClause(" (NOT EXISTS(b))");
assertExistsPropertyEquals("b", theQuery, true);
theQuery = ResourceWebScriptHelper.getWhereClause("( NOT EXISTS(b))");
theQuery = getWhereClause("( NOT EXISTS(b))");
assertExistsPropertyEquals("b", theQuery, true);
theQuery = ResourceWebScriptHelper.getWhereClause(" ( NOT EXISTS(b))");
theQuery = getWhereClause(" ( NOT EXISTS(b))");
assertExistsPropertyEquals("b", theQuery, true);
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("(exists folder)");
theQuery = getWhereClause("(exists folder)");
fail("Should throw an InvalidQueryException, 'folder' should have a bracket around it");
}
catch (InvalidQueryException error)
@@ -137,7 +138,7 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(/target/folder) AND NOT EXISTS(/target/site))");
theQuery = getWhereClause("(EXISTS(/target/folder) AND NOT EXISTS(/target/site))");
assertNotNull(theQuery);
CommonTree tree = theQuery.getTree();
assertNotNull(tree);
@@ -166,7 +167,7 @@ public class WhereTests {
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(/target/folder)OR EXISTS(/target/site))");
theQuery = getWhereClause("(EXISTS(/target/folder)OR EXISTS(/target/site))");
fail("Should throw an InvalidQueryException, the OR should have a space before it.");
}
catch (InvalidQueryException error)
@@ -174,7 +175,7 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("(NOT EXISTS(/target/folder) OR EXISTS(/target/site))");
theQuery = getWhereClause("(NOT EXISTS(/target/folder) OR EXISTS(/target/site))");
QueryHelper.walk(theQuery, new WalkerCallbackAdapter(){
@Override
public void exists(String propertyName, boolean negated) {
@@ -196,7 +197,7 @@ public class WhereTests {
});
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS ( /target/folder ) OR EXISTS( /target/site ) )");
theQuery = getWhereClause("(EXISTS ( /target/folder ) OR EXISTS( /target/site ) )");
QueryHelper.walk(theQuery, new WalkerCallbackAdapter(){
int i=0;
@Override
@@ -220,7 +221,7 @@ public class WhereTests {
});
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(target/file) AND EXISTS(target/folder) AND EXISTS(target/site))");
theQuery = getWhereClause("(EXISTS(target/file) AND EXISTS(target/folder) AND EXISTS(target/site))");
QueryHelper.walk(theQuery, new WalkerCallbackAdapter(){
int i=0;
@Override
@@ -254,25 +255,25 @@ public class WhereTests {
@Test
public void inClauseTest()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt in (5,8) )");
Query theQuery = getWhereClause("( dueAt in (5,8) )");
inChecks(theQuery, "dueAt", "5", "8");
theQuery = ResourceWebScriptHelper.getWhereClause("( fred/bloggs in (head,elbow) )");
theQuery = getWhereClause("( fred/bloggs in (head,elbow) )");
inChecks(theQuery, "fred/bloggs", "head", "elbow");
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne in (5,8,4) )");
theQuery = getWhereClause("( nextOne in (5,8,4) )");
inChecks(theQuery, "nextOne", "5", "8", "4");
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne in (5,56,fred) )");
theQuery = getWhereClause("( nextOne in (5,56,fred) )");
inChecks(theQuery, "nextOne", "5", "56", "fred");
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne in (5,56,'fred&') )");
theQuery = getWhereClause("( nextOne in (5,56,'fred&') )");
inChecks(theQuery, "nextOne", "5", "56", "fred&");
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne in ('me , you',56,egg) )");
theQuery = getWhereClause("( nextOne in ('me , you',56,egg) )");
inChecks(theQuery, "nextOne", "me , you", "56", "egg");
theQuery = ResourceWebScriptHelper.getWhereClause("( NOT nextOne in (5,56,fred, king, kong, 'fred\\'^') )");
theQuery = getWhereClause("( NOT nextOne in (5,56,fred, king, kong, 'fred\\'^') )");
CommonTree tree = theQuery.getTree();
assertNotNull(tree);
QueryHelper.walk(theQuery, new WalkerCallbackAdapter(){
@@ -293,15 +294,15 @@ public class WhereTests {
@Test
public void betweenClauseTest()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt between (5,8) )");
Query theQuery = getWhereClause("( dueAt between (5,8) )");
betweenChecks(theQuery, "dueAt", "5", "8");
theQuery = ResourceWebScriptHelper.getWhereClause("( fred/bloggs between (head,elbow) )");
theQuery = getWhereClause("( fred/bloggs between (head,elbow) )");
betweenChecks(theQuery, "fred/bloggs", "head", "elbow");
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne between (5,8,4) )");
theQuery = getWhereClause("( nextOne between (5,8,4) )");
fail("Should throw an InvalidQueryException, between can have only two values.");
}
catch (InvalidQueryException error)
@@ -311,7 +312,7 @@ public class WhereTests {
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("( nextOne between 5,8 )");
theQuery = getWhereClause("( nextOne between 5,8 )");
fail("Should throw an InvalidQueryException, Need brackets.");
}
catch (InvalidQueryException error)
@@ -319,7 +320,7 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("(NOT dueAt between (5,8) AND nextOne between (green,blue))");
theQuery = getWhereClause("(NOT dueAt between (5,8) AND nextOne between (green,blue))");
QueryHelper.walk(theQuery, new WalkerCallbackAdapter(){
@Override
public void between(String property, String firstVal, String secondVal, boolean negated) {
@@ -349,18 +350,18 @@ public class WhereTests {
@Test
public void matchesClauseTest()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause("(fred matches(bob))");
Query theQuery = getWhereClause("(fred matches(bob))");
matchesChecks(theQuery, "fred", "bob");
theQuery = ResourceWebScriptHelper.getWhereClause("( king/kong/hair/shoulders/knees/toes matches ('fred%') )");
theQuery = getWhereClause("( king/kong/hair/shoulders/knees/toes matches ('fred%') )");
matchesChecks(theQuery, "king/kong/hair/shoulders/knees/toes", "fred%");
theQuery = ResourceWebScriptHelper.getWhereClause("( niceone matches (bob) )");
theQuery = getWhereClause("( niceone matches (bob) )");
matchesChecks(theQuery, "niceone", "bob");
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("( fred matches bob )");
theQuery = getWhereClause("( fred matches bob )");
fail("Should throw an InvalidQueryException, Need brackets.");
}
catch (InvalidQueryException error)
@@ -373,25 +374,25 @@ public class WhereTests {
@Test
public void comparisonClauseTest()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt > '12.04.345' )");
Query theQuery = getWhereClause("( dueAt > '12.04.345' )");
int comparisonOperator = WhereClauseParser.GREATERTHAN;
comparisonChecks(theQuery, comparisonOperator, "dueAt", "12.04.345");
theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt >= '12.04.345' )");
theQuery = getWhereClause("( dueAt >= '12.04.345' )");
comparisonOperator = WhereClauseParser.GREATERTHANOREQUALS;
comparisonChecks(theQuery, comparisonOperator, "dueAt", "12.04.345");
theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt < '12.04.345' )");
theQuery = getWhereClause("( dueAt < '12.04.345' )");
comparisonOperator = WhereClauseParser.LESSTHAN;
comparisonChecks(theQuery, comparisonOperator, "dueAt", "12.04.345");
theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt <= '12.04.345' )");
theQuery = getWhereClause("( dueAt <= '12.04.345' )");
comparisonOperator = WhereClauseParser.LESSTHANOREQUALS;
comparisonChecks(theQuery, comparisonOperator, "dueAt", "12.04.345");
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("( Fred/Bloggs = %$NICE&* )");
theQuery = getWhereClause("( Fred/Bloggs = %$NICE&* )");
fail("Should throw an InvalidQueryException, needs single quotes");
}
catch (InvalidQueryException error)
@@ -399,13 +400,13 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("( Fred/Bloggs = '%$NICE&*' )");
theQuery = getWhereClause("( Fred/Bloggs = '%$NICE&*' )");
comparisonOperator = WhereClauseParser.EQUALS;
comparisonChecks(theQuery, comparisonOperator, "Fred/Bloggs", "%$NICE&*");
try
{
theQuery = ResourceWebScriptHelper.getWhereClause("( Ken = (456) )");
theQuery = getWhereClause("( Ken = (456) )");
fail("Should throw an InvalidQueryException, needs single quotes no brackets");
}
catch (InvalidQueryException error)
@@ -413,15 +414,15 @@ public class WhereTests {
//this is correct
}
theQuery = ResourceWebScriptHelper.getWhereClause("( Ken = '456' )");
theQuery = getWhereClause("( Ken = '456' )");
comparisonOperator = WhereClauseParser.EQUALS;
comparisonChecks(theQuery, comparisonOperator, "Ken", "456");
theQuery = ResourceWebScriptHelper.getWhereClause("( DogHouse = 'Cat\\\'s House' )");
theQuery = getWhereClause("( DogHouse = 'Cat\\\'s House' )");
comparisonOperator = WhereClauseParser.EQUALS;
comparisonChecks(theQuery, comparisonOperator, "DogHouse", "Cat\\\'s House");
theQuery = ResourceWebScriptHelper.getWhereClause("( KING_KONG >= 'Mighty Mouse' )");
theQuery = getWhereClause("( KING_KONG >= 'Mighty Mouse' )");
comparisonOperator = WhereClauseParser.GREATERTHANOREQUALS;
comparisonChecks(theQuery, comparisonOperator, "KING_KONG", "Mighty Mouse");
@@ -442,25 +443,25 @@ public class WhereTests {
@Test
public void getChildrenTests()
{
Query theQuery = ResourceWebScriptHelper.getWhereClause("(fred matches(bob))");
Query theQuery = getWhereClause("(fred matches(bob))");
assertNotNull(theQuery);
CommonTree tree = theQuery.getTree();
assertNotNull(tree);
assertTrue(2 == QueryHelper.getChildren(tree).size());
theQuery = ResourceWebScriptHelper.getWhereClause("( dueAt between (5,8) )");
theQuery = getWhereClause("( dueAt between (5,8) )");
assertNotNull(theQuery);
tree = theQuery.getTree();
assertNotNull(tree);
assertTrue(3 == QueryHelper.getChildren(tree).size());
theQuery = ResourceWebScriptHelper.getWhereClause("(NOT EXISTS(b))");
theQuery = getWhereClause("(NOT EXISTS(b))");
assertNotNull(theQuery);
tree = theQuery.getTree();
assertNotNull(tree);
assertTrue(1 == QueryHelper.getChildren(tree).size());
theQuery = ResourceWebScriptHelper.getWhereClause("(EXISTS(/target/folder) AND EXISTS(/target/site))");
theQuery = getWhereClause("(EXISTS(/target/folder) AND EXISTS(/target/site))");
assertNotNull(theQuery);
tree = theQuery.getTree();
assertNotNull(tree);

View File

@@ -39,6 +39,7 @@ import static org.mockito.Mockito.when;
import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.rest.framework.tools.ResponseWriter;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ApiWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete;
@@ -70,17 +71,17 @@ public class WithResponseTest
@Test
public void testDefaults() throws Exception
{
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WithResponse callBack = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
assertEquals(Status.STATUS_OK, callBack.getStatus());
assertEquals(ApiAssistant.DEFAULT_JSON_CONTENT, callBack.getContentInfo());
assertEquals(ApiAssistant.CACHE_NEVER, callBack.getCache());
assertEquals(ResponseWriter.DEFAULT_JSON_CONTENT, callBack.getContentInfo());
assertEquals(ResponseWriter.CACHE_NEVER, callBack.getCache());
assertTrue(callBack.getHeaders().isEmpty());
}
@Test
public void testSetHeader() throws Exception
{
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WithResponse callBack = new WithResponse(Status.STATUS_OK,ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
callBack.setHeader("king", "can");
callBack.setHeader("king", "kong");
assertTrue(callBack.getHeaders().size() == 1);
@@ -92,7 +93,7 @@ public class WithResponseTest
@Test
public void testAddHeader() throws Exception
{
WithResponse callBack = new WithResponse(Status.STATUS_OK,ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WithResponse callBack = new WithResponse(Status.STATUS_OK,ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
callBack.addHeader("king", "can");
callBack.addHeader("king", "kong");
assertTrue(callBack.getHeaders().size() == 1);
@@ -105,7 +106,7 @@ public class WithResponseTest
@Test
public void testSetters() throws Exception
{
WithResponse callBack = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WithResponse callBack = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
callBack.setStatus(Status.STATUS_GONE);
Cache myCache = new Cache(new Description.RequiredCache()
{
@@ -142,7 +143,7 @@ public class WithResponseTest
{
AbstractResourceWebScript responseWriter = new ResourceWebScriptDelete();
responseWriter.setAssistant(new ApiAssistant());
WithResponse wr = new WithResponse(Status.STATUS_OK, ApiAssistant.DEFAULT_JSON_CONTENT, ApiAssistant.CACHE_NEVER);
WithResponse wr = new WithResponse(Status.STATUS_OK, ResponseWriter.DEFAULT_JSON_CONTENT, ResponseWriter.CACHE_NEVER);
WebScriptResponse response = mock(WebScriptResponse.class);

View File

@@ -0,0 +1,539 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.tools;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.resource.parameters.InvalidSelectException;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.SortColumn;
import org.alfresco.rest.framework.tests.core.ParamsExtender;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.WebScriptRequest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Test the RecognizedParamsExtractor
*
* @author Gethin James
*/
public class RecognizedParamsExtractorTest implements RecognizedParamsExtractor
{
@Test
public void getFilterTest()
{
BeanPropertiesFilter theFilter = getFilter(null);
assertNotNull(theFilter);
assertTrue("Null passed in so must return the default BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(theFilter.getClass()));
theFilter = getFilter("bob");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
theFilter = getFilter("50,fred,b.z");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
theFilter = getFilter("50,fred,");
assertNotNull(theFilter);
assertTrue("Must return the BeanPropertiesFilter class", theFilter instanceof BeanPropertiesFilter);
}
@Test
public void getSortingTest()
{
List<SortColumn> theSort = getSort(null);
assertNotNull(theSort);
assertTrue("Null passed in so empty sort list should be returned.", theSort.isEmpty());
theSort = getSort("name ASC");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
theSort = getSort("name ");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
theSort = getSort("name DESC");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
theSort = getSort("name desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
theSort = getSort("name,age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = getSort(" name, age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = getSort("name DESC, age desc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("name", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
assertEquals("age", theSort.get(1).column);
assertTrue(!theSort.get(1).asc); //desc
theSort = getSort("age Desc, name Asc");
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 2);
assertEquals("age", theSort.get(0).column);
assertTrue(!theSort.get(0).asc); //desc
assertEquals("name", theSort.get(1).column);
assertTrue(theSort.get(1).asc);
theSort = getSort("name des"); //invalid, should be desc
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc); //Defaults to ascending because the sort order was invalid
theSort = getSort("name asc,"); //invalid, should be desc
assertNotNull(theSort);
assertTrue("Must have a value for column: NAME", !theSort.isEmpty());
assertTrue(theSort.size() == 1);
assertEquals("name", theSort.get(0).column);
assertTrue(theSort.get(0).asc);
}
@Test
public void getIncludeClauseTest()
{
getClauseTest("include");
}
@Test
public void getSelectClauseTest()
{
getClauseTest("select");
}
// at the moment select and include are parsed the same way, hence common/shared test
private void getClauseTest(String paramName)
{
List<String> theClause = getCorrectClause(paramName, null);
assertNotNull(theClause);
assertFalse("Null passed in so nothing in the "+paramName, theClause.size() > 0);
try
{
theClause = getCorrectClause(paramName, ",,,");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "(,,,");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "(,,,)");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "x/,z");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "/x'n,/z");
fail("Should throw an InvalidSelectException");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "/foo/0");
fail("Should throw an InvalidSelectException. Legal identifiers must start with a letter not zero");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "/");
fail("Should throw an InvalidSelectException. No identifier specified.");
}
catch (InvalidSelectException error)
{
//this is correct
}
try
{
theClause = getCorrectClause(paramName, "path, isLink");
fail("Should throw an InvalidSelectException. No identifier specified.");
}
catch (InvalidSelectException error)
{
//this is correct
}
theClause = getCorrectClause(paramName, "king/kong");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("king/kong",theClause.get(0));
theClause = getCorrectClause(paramName, "x,y");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("x",theClause.get(0));
assertEquals("y",theClause.get(1));
theClause = getCorrectClause(paramName, "x,/z");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("x",theClause.get(0));
assertEquals("/z",theClause.get(1));
theClause = getCorrectClause(paramName, "/b");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/b",theClause.get(0));
theClause = getCorrectClause(paramName, "/be,/he");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("/be",theClause.get(0));
assertEquals("/he",theClause.get(1));
theClause = getCorrectClause(paramName, "/king/kong");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/king/kong",theClause.get(0));
theClause = getCorrectClause(paramName, "/name,/person/age");
assertTrue("has a valid "+paramName, theClause.size() == 2);
assertEquals("/name",theClause.get(0));
assertEquals("/person/age",theClause.get(1));
theClause = getCorrectClause(paramName, "/foo");
assertTrue("has a valid select",theClause.size() == 1);
assertEquals("/foo",theClause.get(0));
theClause = getCorrectClause(paramName, "/foo/anArray/x");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/foo/anArray/x",theClause.get(0));
theClause = getCorrectClause(paramName, "/foo/anArray/x,/person/age,/eggs/bacon/sausage,/p");
assertTrue("has a valid "+paramName, theClause.size() == 4);
assertEquals("/foo/anArray/x",theClause.get(0));
assertEquals("/person/age",theClause.get(1));
assertEquals("/eggs/bacon/sausage",theClause.get(2));
assertEquals("/p",theClause.get(3));
theClause = getCorrectClause(paramName, "/foo/_bar ");
assertTrue("has a valid "+paramName, theClause.size() == 1);
assertEquals("/foo/_bar",theClause.get(0));
}
private List<String> getCorrectClause(String paramName, String paramValue)
{
if (paramName.equalsIgnoreCase("include"))
{
return getIncludeClause(paramValue);
}
else if (paramName.equalsIgnoreCase("select"))
{
return getSelectClause(paramValue);
}
fail("Unexpected clause: "+paramName);
return null;
}
@Test
public void getRelationFilterTest()
{
Map<String, BeanPropertiesFilter> theFilter = getRelationFilter(null);
assertNotNull(theFilter);
assertTrue("Null passed in so nothing to filter.",theFilter.isEmpty());
theFilter = getRelationFilter("bob");
assertNotNull(theFilter);
assertTrue("Must be a single relationship", theFilter.size() == 1);
assertTrue("Must be a single relationship called bob", theFilter.containsKey("bob"));
BeanPropertiesFilter aFilter = theFilter.get("bob");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
theFilter = getRelationFilter("bob,hope");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
assertTrue("Must have hope.", theFilter.containsKey("hope"));
aFilter = theFilter.get("hope");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
theFilter = getRelationFilter("bob(name),hope");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
assertTrue("Must have bob.", theFilter.containsKey("bob"));
aFilter = theFilter.get("bob");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
theFilter = getRelationFilter("bob,hope(age,name)");
assertNotNull(theFilter);
assertTrue("Must be a two relationships", theFilter.size() == 2);
aFilter = theFilter.get("bob");
assertTrue("No bean properties specified so need a BeanPropertiesFilter.ALLOW_ALL class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
aFilter = theFilter.get("hope");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
theFilter = getRelationFilter("bob(name,age),nohope,hope(height,width)");
assertNotNull(theFilter);
assertTrue("Must be a three relationships", theFilter.size() == 3);
aFilter = theFilter.get("bob");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
aFilter = theFilter.get("nohope");
assertTrue("No bean properties specified so need a ReturnAllBeanProperties class", BeanPropertiesFilter.AllProperties.class.equals(aFilter.getClass()));
aFilter = theFilter.get("hope");
assertTrue("Bean properties specified so must be an BeanPropertiesFilter class", BeanPropertiesFilter.class.equals(aFilter.getClass()));
}
@Test
public void findPagingTest()
{
WebScriptRequest request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("34");
when(request.getParameter("maxItems")).thenReturn("50");
Paging pagin = findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == 34);
assertTrue(pagin.getMaxItems() == 50);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn(null);
when(request.getParameter("maxItems")).thenReturn(null);
pagin = findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
assertTrue(pagin.getMaxItems() == Paging.DEFAULT_MAX_ITEMS);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("55");
pagin = findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getSkipCount() == 55);
assertTrue(pagin.getMaxItems() == Paging.DEFAULT_MAX_ITEMS);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn(null);
when(request.getParameter("maxItems")).thenReturn("45");
pagin = findPaging(request);
assertNotNull(pagin);
assertTrue(pagin.getMaxItems() == 45);
assertTrue(pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("apple");
when(request.getParameter("maxItems")).thenReturn("pear");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("0");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
//Test Case cloud-2198
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("a");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("s");
when(request.getParameter("maxItems")).thenReturn("5");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("0");
when(request.getParameter("maxItems")).thenReturn("-2");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("skipCount")).thenReturn("-3");
when(request.getParameter("maxItems")).thenReturn("5");
try
{
pagin = findPaging(request);
fail("Should not get here.");
}
catch (InvalidArgumentException iae)
{
assertNotNull(iae); // Must throw this exceptions
}
request = mock(WebScriptRequest.class);
when(request.getParameter("maxItems")).thenReturn("5");
pagin = findPaging(request);
assertNotNull(pagin);
assertTrue("skip count defaults to 0", pagin.getSkipCount() == Paging.DEFAULT_SKIP_COUNT);
//End of Test Case cloud-2198
}
@Test
public void paramsTest()
{
Map<String,List<String>> mockParams = new HashMap<String,List<String>>();
mockParams.put("age", Arrays.asList("23","45"));
mockParams.put("name", Arrays.asList("fred"));
WebScriptRequest request = mockRequest(mockParams);
Map<String, String[]> params = getRequestParameters(request);
assertNotNull(params);
Params paramObj = ParamsExtender.valueOf(params);
assertNotNull(paramObj);
String aValue = paramObj.getParameter("age");
assertEquals("23", aValue);
aValue = paramObj.getParameter("name");
assertEquals("fred", aValue);
}
private WebScriptRequest mockRequest(final Map<String,List<String>> params)
{
final String[] paramNames = params.keySet().toArray(new String[]{});
WebScriptRequest request = mock(WebScriptRequest.class);
when(request.getParameterNames()).thenReturn(paramNames);
when(request.getParameterValues(anyString())).thenAnswer(new Answer<String[]>() {
@Override
public String[] answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return params.get((String) args[0]).toArray(new String[]{});
}
});
return request;
}
}

View File

@@ -43,7 +43,7 @@ import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.alfresco.rest.framework.tools.RecognizedParamsExtractor;
import org.alfresco.rest.workflow.api.Processes;
import org.alfresco.rest.workflow.api.impl.ProcessesImpl;
import org.alfresco.rest.workflow.api.model.ProcessInfo;
@@ -67,7 +67,7 @@ import junit.framework.TestCase;
*
* @author Dmitry Velichkevich
*/
public class ProcessesImplTest extends TestCase
public class ProcessesImplTest extends TestCase implements RecognizedParamsExtractor
{
private static final String[] CONFIG_LOCATIONS = new String[ApplicationContextHelper.CONFIG_LOCATIONS.length + 2];
static
@@ -224,7 +224,7 @@ public class ProcessesImplTest extends TestCase
private CollectionWithPagingInfo<ProcessInfo> queryMatchesProcesses(String matchesString)
{
Query query = ResourceWebScriptHelper.getWhereClause(String.format(QUERY_WORKFLOWDESCRIPTION_MATCHES, matchesString));
Query query = getWhereClause(String.format(QUERY_WORKFLOWDESCRIPTION_MATCHES, matchesString));
Parameters parameters = Params.valueOf(new RecognizedParams(null, Paging.valueOf(0, ACTIVE_WORKFLOWS_INITIAL_AMOUNT), null, null, null, null, query, null, false), null, null, null);
return processes.getProcesses(parameters);
@@ -232,7 +232,7 @@ public class ProcessesImplTest extends TestCase
private CollectionWithPagingInfo<ProcessInfo> queryActiveProcessesAndAssertResult(int skipCount, int maxItems)
{
Query query = ResourceWebScriptHelper.getWhereClause(QUERY_STATUS_ACTIVE);
Query query = getWhereClause(QUERY_STATUS_ACTIVE);
Parameters parameters = Params.valueOf(new RecognizedParams(null, Paging.valueOf(skipCount, maxItems), null, null, null, null, query, null, false), null, null, null);
CollectionWithPagingInfo<ProcessInfo> result = processes.getProcesses(parameters);