ACE-4469: Merged BRANCHES/DEV/HEAD-SFS (cherry picked) to HEAD

113520: SFS-179: Added multipart upload support into Public API framework.
   114561: SFS-179: Added tests for upload API, as well as minor fixes.
   114732: SFS-179: Changed the assert import from 3.X to 4.X.
   114734: SFS-179: Added unit tests for Public API framework multiPart support.
   114735: SFS-179: Fixed unit test failure.
- Also removed mergeinfo added in r112639

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@114736 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2015-10-19 23:25:59 +00:00
parent a4c853d923
commit 8855ef66f8
19 changed files with 760 additions and 92 deletions

View File

@@ -1,3 +1,4 @@
package org.alfresco.rest.api; package org.alfresco.rest.api;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@@ -10,50 +11,80 @@ import javax.servlet.http.HttpServletRequestWrapper;
public class PublicApiHttpServletRequest extends HttpServletRequestWrapper public class PublicApiHttpServletRequest extends HttpServletRequestWrapper
{ {
public PublicApiHttpServletRequest(HttpServletRequest request) throws IOException private static final String HEADER_CONTENT_TYPE = "Content-Type";
{ public static final String MULTIPART_FORM_DATA = "multipart/form-data";
super(getWrappedHttpServletRequest(request));
}
public void resetInputStream() throws IOException public PublicApiHttpServletRequest(HttpServletRequest request) throws IOException
{ {
ServletInputStream stream = getInputStream(); super(getWrappedHttpServletRequest(request));
stream.reset(); }
}
private static HttpServletRequest getWrappedHttpServletRequest(HttpServletRequest request) throws IOException
{
final PublicApiServletInputStream sis = new PublicApiServletInputStream(request.getInputStream());
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request)
{
public ServletInputStream getInputStream() throws java.io.IOException
{
return sis;
}
};
return wrapper;
}
private static class PublicApiServletInputStream extends ServletInputStream
{
private BufferedInputStream in;
PublicApiServletInputStream(InputStream in) public void resetInputStream() throws IOException
{ {
this.in = new BufferedInputStream(in); ServletInputStream stream = getInputStream();
this.in.mark(8096); if (stream.markSupported())
} {
stream.reset();
}
}
@Override private static HttpServletRequest getWrappedHttpServletRequest(HttpServletRequest request) throws IOException
public int read() throws IOException {
{ //TODO is it really necessary to wrap the request into a BufferedInputStream?
return in.read(); // If not, then we could remove the check for multipart upload.
} // The check is needed as we get an IOException (Resetting to invalid mark) for files more than 8193 bytes.
boolean resetSupported = true;
String contentType = request.getHeader(HEADER_CONTENT_TYPE);
if (contentType != null && contentType.startsWith(MULTIPART_FORM_DATA))
{
resetSupported = false;
}
final PublicApiServletInputStream sis = new PublicApiServletInputStream(request.getInputStream(), resetSupported);
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request)
{
public ServletInputStream getInputStream() throws java.io.IOException
{
return sis;
}
};
return wrapper;
}
@Override private static class PublicApiServletInputStream extends ServletInputStream
public void reset() throws IOException {
{ private final InputStream in;
in.reset(); private final boolean resetSupported;
}
} PublicApiServletInputStream(InputStream in, boolean resetSupported)
{
this.resetSupported = resetSupported;
if (resetSupported)
{
this.in = new BufferedInputStream(in);
this.in.mark(8096);
}
else
{
this.in = in;
}
}
@Override
public int read() throws IOException
{
return in.read();
}
@Override
public void reset() throws IOException
{
in.reset();
}
@Override
public boolean markSupported()
{
return resetSupported;
}
}
} }

View File

@@ -27,6 +27,8 @@ import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.UniqueId; import org.alfresco.rest.framework.resource.UniqueId;
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
@@ -58,13 +60,15 @@ public class ResourceInspector
ALL_ENTITY_RESOURCE_INTERFACES.add(EntityResourceAction.Update.class); ALL_ENTITY_RESOURCE_INTERFACES.add(EntityResourceAction.Update.class);
ALL_ENTITY_RESOURCE_INTERFACES.add(EntityResourceAction.Delete.class); ALL_ENTITY_RESOURCE_INTERFACES.add(EntityResourceAction.Delete.class);
ALL_ENTITY_RESOURCE_INTERFACES.add(BinaryResourceAction.Read.class); ALL_ENTITY_RESOURCE_INTERFACES.add(BinaryResourceAction.Read.class);
ALL_ENTITY_RESOURCE_INTERFACES.add(MultiPartResourceAction.Create.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Create.class); ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Create.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Read.class); ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Read.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.ReadById.class); ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.ReadById.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Update.class); ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Update.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Delete.class); ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(RelationshipResourceAction.Delete.class);
ALL_RELATIONSHIP_RESOURCE_INTERFACES.add(MultiPartRelationshipResourceAction.Create.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Read.class); ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Read.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Delete.class); ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Delete.class);
ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Update.class); ALL_PROPERTY_RESOURCE_INTERFACES.add(BinaryResourceAction.Update.class);
@@ -90,6 +94,7 @@ public class ResourceInspector
findOperation(EntityResourceAction.ReadById.class, HttpMethod.GET, helper); findOperation(EntityResourceAction.ReadById.class, HttpMethod.GET, helper);
findOperation(EntityResourceAction.Update.class, HttpMethod.PUT, helper); findOperation(EntityResourceAction.Update.class, HttpMethod.PUT, helper);
findOperation(EntityResourceAction.Delete.class, HttpMethod.DELETE, helper); findOperation(EntityResourceAction.Delete.class, HttpMethod.DELETE, helper);
findOperation(MultiPartResourceAction.Create.class, HttpMethod.POST, helper);
if (resource.isAnnotationPresent(WebApiDeleted.class)) if (resource.isAnnotationPresent(WebApiDeleted.class))
{ {
@@ -189,7 +194,8 @@ public class ResourceInspector
findOperation(RelationshipResourceAction.Read.class, HttpMethod.GET, helper); findOperation(RelationshipResourceAction.Read.class, HttpMethod.GET, helper);
findOperation(RelationshipResourceAction.ReadById.class, HttpMethod.GET, helper); findOperation(RelationshipResourceAction.ReadById.class, HttpMethod.GET, helper);
findOperation(RelationshipResourceAction.Update.class, HttpMethod.PUT, helper); findOperation(RelationshipResourceAction.Update.class, HttpMethod.PUT, helper);
findOperation(RelationshipResourceAction.Delete.class, HttpMethod.DELETE, helper); findOperation(RelationshipResourceAction.Delete.class, HttpMethod.DELETE, helper);
findOperation(MultiPartRelationshipResourceAction.Create.class, HttpMethod.POST, helper);
if (resource.isAnnotationPresent(WebApiDeleted.class)) if (resource.isAnnotationPresent(WebApiDeleted.class))
{ {

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.framework.resource.actions.interfaces;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.springframework.extensions.webscripts.servlet.FormData;
/**
* @author Jamal Kaabi-Mofrad
*/
public interface MultiPartRelationshipResourceAction
{
/**
* HTTP POST - Upload file content and meta-data into repository
*/
public static interface Create<E> extends ResourceAction
{
public E create(String entityResourceId, FormData formData, Parameters parameters);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.framework.resource.actions.interfaces;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.springframework.extensions.webscripts.servlet.FormData;
/**
* @author Jamal Kaabi-Mofrad
*/
public interface MultiPartResourceAction
{
/**
* HTTP POST - Upload file content and meta-data into repository
*/
public static interface Create<E> extends ResourceAction
{
public E create(FormData formData, Parameters parameters);
}
}

View File

@@ -7,6 +7,7 @@ import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.resource.content.BasicContentInfo; import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.parameters.where.Query; import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.formula.functions.T;
import org.springframework.extensions.webscripts.Status;
/** /**
@@ -90,5 +91,12 @@ public interface Parameters
* Gets the basic information about content, typically taken from a HTTPServletRequest. * Gets the basic information about content, typically taken from a HTTPServletRequest.
* @return BasicContentInfo the content info * @return BasicContentInfo the content info
*/ */
BasicContentInfo getContentInfo(); BasicContentInfo getContentInfo();
/**
* Gets Web Script status
*
* @return {@link Status}
*/
public Status getStatus();
} }

View File

@@ -14,6 +14,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.QueryImpl;
import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.ConvertUtils;
import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.formula.functions.T;
import org.springframework.extensions.webscripts.Status;
/** /**
* Parameters passed in from a Rest client for use in calls to the rest api. * Parameters passed in from a Rest client for use in calls to the rest api.
@@ -29,7 +30,8 @@ public class Params implements Parameters
private final RecognizedParams recognizedParams; private final RecognizedParams recognizedParams;
private final String addressedProperty; private final String addressedProperty;
private final BasicContentInfo contentInfo; private final BasicContentInfo contentInfo;
private final Status status;
//Constants //Constants
private static final RecognizedParams NULL_PARAMS = new RecognizedParams(null, null, null, null, null, null, null); private static final RecognizedParams NULL_PARAMS = new RecognizedParams(null, null, null, null, null, null, null);
private static final BasicContentInfo DEFAULT_CONTENT_INFO = new ContentInfoImpl(MimetypeMap.MIMETYPE_BINARY, "UTF-8", -1, null); private static final BasicContentInfo DEFAULT_CONTENT_INFO = new ContentInfoImpl(MimetypeMap.MIMETYPE_BINARY, "UTF-8", -1, null);
@@ -44,6 +46,7 @@ public class Params implements Parameters
this.recognizedParams = recognizedParams; this.recognizedParams = recognizedParams;
this.addressedProperty = addressedProperty; this.addressedProperty = addressedProperty;
this.contentInfo = contentInfo==null?DEFAULT_CONTENT_INFO:contentInfo; this.contentInfo = contentInfo==null?DEFAULT_CONTENT_INFO:contentInfo;
this.status = new Status();
} }
public static Params valueOf(BeanPropertiesFilter paramFilter, String entityId) public static Params valueOf(BeanPropertiesFilter paramFilter, String entityId)
@@ -196,10 +199,17 @@ public class Params implements Parameters
} }
@Override @Override
public BasicContentInfo getContentInfo() { public BasicContentInfo getContentInfo()
return contentInfo; {
} return contentInfo;
}
@Override
public Status getStatus()
{
return status;
}
/** /**
* A formal set of params that any rest service could potentially have passed in as request params * A formal set of params that any rest service could potentially have passed in as request params
*/ */

View File

@@ -72,7 +72,14 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
{ {
respons.put("toSerialize", result); respons.put("toSerialize", result);
respons.put("contentInfo", contentInfo); respons.put("contentInfo", contentInfo);
setSuccessResponseStatus(res); if (params.getStatus().getRedirect())
{
res.setStatus(params.getStatus().getCode());
}
else
{
setSuccessResponseStatus(res);
}
} }
}); });

View File

@@ -13,6 +13,8 @@ import org.alfresco.rest.framework.core.exceptions.DeletedResourceException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException; import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params; import org.alfresco.rest.framework.resource.parameters.Params;
@@ -20,7 +22,9 @@ import org.alfresco.rest.framework.resource.parameters.Params.RecognizedParams;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptRequestImpl;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.extensions.webscripts.servlet.FormData;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
/** /**
@@ -55,7 +59,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
} }
else else
{ {
Object postedObj = extractObjFromJson(resourceMeta, req); Object postedObj = processRequest(resourceMeta, req);
return Params.valueOf(null, params, postedObj); return Params.valueOf(null, params, postedObj);
} }
case RELATIONSHIP: case RELATIONSHIP:
@@ -63,18 +67,33 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID); String relationshipId = req.getServiceMatch().getTemplateVars().get(ResourceLocator.RELATIONSHIP_ID);
if (StringUtils.isNotBlank(relationshipId)) if (StringUtils.isNotBlank(relationshipId))
{ {
throw new UnsupportedResourceOperationException("POST is executed against the collection URL"); throw new UnsupportedResourceOperationException("POST is executed against the collection URL");
} }
else else
{ {
Object postedRel = extractObjFromJson(resourceMeta, req); Object postedRel = processRequest(resourceMeta, req);
return Params.valueOf(entityId,params,postedRel); return Params.valueOf(entityId, params, postedRel);
} }
default: default:
throw new UnsupportedResourceOperationException("POST not supported for Actions"); throw new UnsupportedResourceOperationException("POST not supported for Actions");
} }
} }
/**
* If the request content-type is <i><b>multipart/form-data</b></i> then it
* returns the {@link FormData}, otherwise it tries to extract the required
* object from the JSON payload.
*/
private Object processRequest(ResourceMetadata resourceMeta, WebScriptRequest req)
{
if (WebScriptRequestImpl.MULTIPART_FORM_DATA.equals(req.getContentType()))
{
return (FormData) req.parseContent();
}
return extractObjFromJson(resourceMeta, req);
}
/** /**
* If the @WebApiParam has been used and set allowMultiple to false then this will get a single entry. It * If the @WebApiParam has been used and set allowMultiple to false then this will get a single entry. It
* should error if an array is passed in. * should error if an array is passed in.
@@ -86,7 +105,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
{ {
List<ResourceParameter> params = resourceMeta.getParameters(HttpMethod.POST); List<ResourceParameter> params = resourceMeta.getParameters(HttpMethod.POST);
Class<?> objType = resourceMeta.getObjectType(HttpMethod.POST); Class<?> objType = resourceMeta.getObjectType(HttpMethod.POST);
if (!params.isEmpty()) if (!params.isEmpty())
{ {
for (ResourceParameter resourceParameter : params) for (ResourceParameter resourceParameter : params)
@@ -125,39 +144,60 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Object executeInternal(ResourceWithMetadata resource, Params params) private Object executeInternal(ResourceWithMetadata resource, Params params)
{ {
final Object resObj = resource.getResource();
switch (resource.getMetaData().getType()) switch (resource.getMetaData().getType())
{ {
case ENTITY: case ENTITY:
if (resource.getMetaData().isDeleted(EntityResourceAction.Create.class)) if (resource.getMetaData().isDeleted(EntityResourceAction.Create.class))
{ {
throw new DeletedResourceException("(DELETE) "+resource.getMetaData().getUniqueId()); throw new DeletedResourceException("(DELETE) " + resource.getMetaData().getUniqueId());
} }
EntityResourceAction.Create<Object> creator = (EntityResourceAction.Create<Object>) resource.getResource();
List<Object> created = creator.create((List<Object>) params.getPassedIn(), params); if (resObj instanceof MultiPartResourceAction.Create<?>)
if (created !=null && created.size() == 1)
{ {
//return just one object instead of an array MultiPartResourceAction.Create<Object> creator = (MultiPartResourceAction.Create<Object>) resObj;
return created.get(0); return creator.create((FormData) params.getPassedIn(), params);
} }
else else
{ {
return wrapWithCollectionWithPaging(created); EntityResourceAction.Create<Object> creator = (EntityResourceAction.Create<Object>) resObj;
List<Object> created = creator.create((List<Object>) params.getPassedIn(), params);
if (created != null && created.size() == 1)
{
// return just one object instead of an array
return created.get(0);
}
else
{
return wrapWithCollectionWithPaging(created);
}
} }
case RELATIONSHIP: case RELATIONSHIP:
if (resource.getMetaData().isDeleted(RelationshipResourceAction.Create.class)) if (resource.getMetaData().isDeleted(RelationshipResourceAction.Create.class))
{ {
throw new DeletedResourceException("(DELETE) "+resource.getMetaData().getUniqueId()); throw new DeletedResourceException("(DELETE) " + resource.getMetaData().getUniqueId());
} }
RelationshipResourceAction.Create<Object> createRelation = (RelationshipResourceAction.Create) resource.getResource();
List<Object> createdRel = createRelation.create(params.getEntityId(), (List<Object>) params.getPassedIn(), params); if (resObj instanceof MultiPartRelationshipResourceAction.Create<?>)
if (createdRel !=null && createdRel.size() == 1)
{ {
//return just one object instead of an array MultiPartRelationshipResourceAction.Create<Object> creator = (MultiPartRelationshipResourceAction.Create<Object>) resObj;
return createdRel.get(0); return creator.create(params.getEntityId(), (FormData) params.getPassedIn(), params);
} }
else else
{ {
return wrapWithCollectionWithPaging(createdRel); RelationshipResourceAction.Create<Object> createRelation = (RelationshipResourceAction.Create<Object>) resource.getResource();
List<Object> createdRel = createRelation.create(params.getEntityId(), (List<Object>) params.getPassedIn(), params);
if (createdRel != null && createdRel.size() == 1)
{
// return just one object instead of an array
return createdRel.get(0);
}
else
{
return wrapWithCollectionWithPaging(createdRel);
}
} }
default: default:
throw new UnsupportedResourceOperationException("POST not supported for Actions"); throw new UnsupportedResourceOperationException("POST not supported for Actions");
@@ -168,7 +208,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
{ {
if (created !=null && created.size() > 1) if (created !=null && created.size() > 1)
{ {
return CollectionWithPagingInfo.asPagedCollection(created.toArray()); return CollectionWithPagingInfo.asPagedCollection(created.toArray());
} }
else else
{ {
@@ -176,7 +216,6 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
} }
} }
@Override @Override
public void execute(final ResourceWithMetadata resource, final Params params, final ExecutionCallback executionCallback) public void execute(final ResourceWithMetadata resource, final Params params, final ExecutionCallback executionCallback)
{ {
@@ -194,6 +233,7 @@ public class ResourceWebScriptPost extends AbstractResourceWebScript implements
} }
}, false, true); }, false, true);
} }
@Override @Override
protected void setSuccessResponseStatus(WebScriptResponse res) protected void setSuccessResponseStatus(WebScriptResponse res)
{ {

View File

@@ -61,6 +61,19 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return response; return response;
} }
protected HttpResponse post(String url, String runAsUser, String body, String queryString, String contentType, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
if (queryString != null)
{
url += queryString;
}
HttpResponse response = publicApiClient.post(getScope(), url, null, null, null, body, contentType);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse getAll(String url, String runAsUser, PublicApiClient.Paging paging, int expectedStatus) throws Exception protected HttpResponse getAll(String url, String runAsUser, PublicApiClient.Paging paging, int expectedStatus) throws Exception
{ {
publicApiClient.setRequestContext(new RequestContext(runAsUser)); publicApiClient.setRequestContext(new RequestContext(runAsUser));

View File

@@ -440,6 +440,17 @@ public class PublicApiClient
return response; return response;
} }
public HttpResponse post(String scope, String entityCollectionName, Object entityId, String relationCollectionName, Object relationshipEntityId,
String body, String contentType) throws IOException
{
HttpResponse response = client.post(getRequestContext(), scope, entityCollectionName, entityId, relationCollectionName,
relationshipEntityId != null ? relationshipEntityId.toString() : null, body, contentType);
logger.debug(response.toString());
return response;
}
public HttpResponse post(String urlSuffix, String body) throws IOException public HttpResponse post(String urlSuffix, String body) throws IOException
{ {
HttpResponse response = client.post(getRequestContext(), urlSuffix, body); HttpResponse response = client.post(getRequestContext(), urlSuffix, body);

View File

@@ -450,20 +450,33 @@ public class PublicApiHttpClient
PatchMethod req = new PatchMethod(url.toString()); PatchMethod req = new PatchMethod(url.toString());
return submitRequest(req, rq); return submitRequest(req, rq);
} }
public HttpResponse post(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId, final String relationCollectionName, final Object relationshipEntityId, final String body) throws IOException public HttpResponse post(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId,
{ final String relationCollectionName, final Object relationshipEntityId, final String body) throws IOException
RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, null); {
String url = endpoint.getUrl(); return post(rq, scope, entityCollectionName, entityId, relationCollectionName, relationshipEntityId, body, "application/json");
}
PostMethod req = new PostMethod(url.toString());
if(body != null) public HttpResponse post(final RequestContext rq, final String scope, final String entityCollectionName, final Object entityId,
{ final String relationCollectionName, final Object relationshipEntityId, final String body, String contentType) throws IOException
StringRequestEntity requestEntity = new StringRequestEntity(body, "application/json", "UTF-8"); {
req.setRequestEntity(requestEntity); RestApiEndpoint endpoint = new RestApiEndpoint(rq.getNetworkId(), scope, entityCollectionName, entityId, relationCollectionName,
} relationshipEntityId, null);
return submitRequest(req, rq); String url = endpoint.getUrl();
}
PostMethod req = new PostMethod(url.toString());
if (body != null)
{
if (contentType == null || contentType.isEmpty())
{
contentType = "application/json";
}
StringRequestEntity requestEntity = new StringRequestEntity(body, contentType, "UTF-8");
req.setRequestEntity(requestEntity);
}
return submitRequest(req, rq);
}
public HttpResponse delete(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId) throws IOException public HttpResponse delete(final Class<?> c, final RequestContext rq, final Object entityId, final Object relationshipEntityId) throws IOException
{ {

View File

@@ -0,0 +1,259 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests.util;
import static org.junit.Assert.assertNotNull;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* <i><b>multipart/form-data</b></i> builder.
*
* @author Jamal Kaabi-Mofrad
*/
public class MultiPartBuilder
{
private FileData fileData;
private String siteId;
private String containerId;
private String destination;
private String uploadDirectory;
private String updateNodeRef;
private String description;
private String contentTypeQNameStr;
private List<String> aspects;
private boolean majorVersion;
private boolean overwrite = true; // If a fileName clashes for a versionable file
private MultiPartBuilder()
{
}
private MultiPartBuilder(MultiPartBuilder that)
{
this.fileData = that.fileData;
this.siteId = that.siteId;
this.containerId = that.containerId;
this.destination = that.destination;
this.uploadDirectory = that.uploadDirectory;
this.updateNodeRef = that.updateNodeRef;
this.description = that.description;
this.contentTypeQNameStr = that.contentTypeQNameStr;
this.aspects = that.aspects;
this.majorVersion = that.majorVersion;
this.overwrite = that.overwrite;
}
public static MultiPartBuilder create()
{
return new MultiPartBuilder();
}
public static MultiPartBuilder copy(MultiPartBuilder copy)
{
return new MultiPartBuilder(copy);
}
public MultiPartBuilder setFileData(FileData fileData)
{
this.fileData = fileData;
return this;
}
public MultiPartBuilder setSiteId(String siteId)
{
this.siteId = siteId;
return this;
}
public MultiPartBuilder setContainerId(String containerId)
{
this.containerId = containerId;
return this;
}
public MultiPartBuilder setDestination(String destination)
{
this.destination = destination;
return this;
}
public MultiPartBuilder setUploadDirectory(String uploadDirectory)
{
this.uploadDirectory = uploadDirectory;
return this;
}
public MultiPartBuilder setUpdateNoderef(String updateNodeRef)
{
this.updateNodeRef = updateNodeRef;
return this;
}
public MultiPartBuilder setDescription(String description)
{
this.description = description;
return this;
}
public MultiPartBuilder setContentTypeQNameStr(String contentTypeQNameStr)
{
this.contentTypeQNameStr = contentTypeQNameStr;
return this;
}
public MultiPartBuilder setAspects(List<String> aspects)
{
this.aspects = aspects;
return this;
}
public MultiPartBuilder setMajorVersion(boolean majorVersion)
{
this.majorVersion = majorVersion;
return this;
}
public MultiPartBuilder setOverwrite(boolean overwrite)
{
this.overwrite = overwrite;
return this;
}
private String getAspects(List<String> aspects)
{
if (aspects != null)
{
StringBuilder sb = new StringBuilder(aspects.size() * 2);
for (String str : aspects)
{
sb.append(str).append(',');
}
sb.deleteCharAt(sb.length() - 1); // remove leading separator
return sb.toString();
}
return null;
}
public static class FileData
{
private final String fileName;
private final File file;
private final String mimetype;
public FileData(String fileName, File file, String mimetype)
{
this.fileName = fileName;
this.file = file;
this.mimetype = mimetype;
}
public String getFileName()
{
return fileName;
}
public File getFile()
{
return file;
}
public String getMimetype()
{
return mimetype;
}
}
public static class MultiPartRequest
{
private final byte[] body;
private final String contentType;
private final long contentLength;
public MultiPartRequest(byte[] body, String contentType, long contentLength)
{
this.body = body;
this.contentType = contentType;
this.contentLength = contentLength;
}
public byte[] getBody()
{
return body;
}
public String getContentType()
{
return contentType;
}
public long getContentLength()
{
return contentLength;
}
}
public MultiPartRequest build() throws IOException
{
assertNotNull(fileData);
List<Part> parts = new ArrayList<>();
parts.add(new FilePart("filedata", fileData.getFileName(), fileData.getFile(), fileData.getMimetype(), null));
addPartIfNotNull(parts, "filename", fileData.getFileName());
addPartIfNotNull(parts, "siteid", siteId);
addPartIfNotNull(parts, "containerid", containerId);
addPartIfNotNull(parts, "destination", destination);
addPartIfNotNull(parts, "uploaddirectory", uploadDirectory);
addPartIfNotNull(parts, "updatenoderef", updateNodeRef);
addPartIfNotNull(parts, "description", description);
addPartIfNotNull(parts, "contenttype", contentTypeQNameStr);
addPartIfNotNull(parts, "aspects", getAspects(aspects));
addPartIfNotNull(parts, "majorversion", Boolean.toString(majorVersion));
addPartIfNotNull(parts, "overwrite", Boolean.toString(overwrite));
MultipartRequestEntity req = new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), new HttpMethodParams());
ByteArrayOutputStream os = new ByteArrayOutputStream();
req.writeRequest(os);
return new MultiPartRequest(os.toByteArray(), req.getContentType(), req.getContentLength());
}
private void addPartIfNotNull(List<Part> list, String partName, String partValue)
{
if (partValue != null)
{
list.add(new StringPart(partName, partValue));
}
}
}

View File

@@ -0,0 +1,22 @@
package org.alfresco.rest.framework.tests.api.mocks;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.springframework.extensions.webscripts.servlet.FormData;
/**
* @author Jamal Kaabi-Mofrad
*/
@EntityResource(name = "multiparttest", title = "multi-part upload test")
public class MultiPartTestEntityResource
implements MultiPartResourceAction.Create<MultiPartTestResponse>
{
@Override
public MultiPartTestResponse create(FormData formData, Parameters parameters)
{
return new MultiPartTestResponse(formData.getParameters().get("filename")[0]);
}
}

View File

@@ -0,0 +1,24 @@
package org.alfresco.rest.framework.tests.api.mocks;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.springframework.extensions.webscripts.servlet.FormData;
/**
* @author Jamal Kaabi-Mofrad
*/
@RelationshipResource(name = "sheepUpload", entityResource = SheepEntityResource.class, title = "Sheep mulitpart upload")
public class MultiPartTestRelationshipResource
implements MultiPartRelationshipResourceAction.Create<MultiPartTestResponse>
{
@Override
public MultiPartTestResponse create(String entityResourceId, FormData formData,
Parameters parameters)
{
return new MultiPartTestResponse(formData.getParameters().get("filename")[0]);
}
}

View File

@@ -0,0 +1,23 @@
package org.alfresco.rest.framework.tests.api.mocks;
/**
* Simple mock pojo for MultiPart response.
*
* @author Jamal Kaabi-Mofrad
*/
public class MultiPartTestResponse
{
private String fileName;
public MultiPartTestResponse(String fileName)
{
this.fileName = fileName;
}
public String getFileName()
{
return this.fileName;
}
}

View File

@@ -26,6 +26,9 @@ import org.alfresco.rest.framework.tests.api.mocks.Farmer;
import org.alfresco.rest.framework.tests.api.mocks.GoatEntityResource; import org.alfresco.rest.framework.tests.api.mocks.GoatEntityResource;
import org.alfresco.rest.framework.tests.api.mocks.Grass; import org.alfresco.rest.framework.tests.api.mocks.Grass;
import org.alfresco.rest.framework.tests.api.mocks.GrassEntityResource; import org.alfresco.rest.framework.tests.api.mocks.GrassEntityResource;
import org.alfresco.rest.framework.tests.api.mocks.MultiPartTestEntityResource;
import org.alfresco.rest.framework.tests.api.mocks.MultiPartTestRelationshipResource;
import org.alfresco.rest.framework.tests.api.mocks.MultiPartTestResponse;
import org.alfresco.rest.framework.tests.api.mocks.Sheep; import org.alfresco.rest.framework.tests.api.mocks.Sheep;
import org.alfresco.rest.framework.tests.api.mocks.SheepBlackSheepResource; import org.alfresco.rest.framework.tests.api.mocks.SheepBlackSheepResource;
import org.alfresco.rest.framework.tests.api.mocks.SheepEntityResource; import org.alfresco.rest.framework.tests.api.mocks.SheepEntityResource;
@@ -83,7 +86,17 @@ public class InspectorTests
assertTrue("FlockEntityResource supports PUT", metaData.supports(HttpMethod.PUT)); assertTrue("FlockEntityResource supports PUT", metaData.supports(HttpMethod.PUT));
assertTrue("FlockEntityResource supports DELETE", metaData.supports(HttpMethod.DELETE)); assertTrue("FlockEntityResource supports DELETE", metaData.supports(HttpMethod.DELETE));
assertTrue("FlockEntityResource does not support POST", !metaData.supports(HttpMethod.POST)); assertTrue("FlockEntityResource does not support POST", !metaData.supports(HttpMethod.POST));
metainfo = ResourceInspector.inspect(MultiPartTestEntityResource.class);
assertTrue("Must be one ResourceMetadata",metainfo.size()==1);
metaData = metainfo.get(0);
assertNotNull(metaData);
assertTrue("MultiPartTestEntityResource support POST", metaData.supports(HttpMethod.POST));
assertFalse("MultiPartTestEntityResource does not supports GET", metaData.supports(HttpMethod.GET));
assertFalse("MultiPartTestEntityResource does not supports PUT", metaData.supports(HttpMethod.PUT));
assertFalse("MultiPartTestEntityResource does not supports DELETE", metaData.supports(HttpMethod.DELETE));
assertTrue("MultiPartTestEntityResource must support MultiPartTestResponse", MultiPartTestResponse.class.equals(metaData.getObjectType(HttpMethod.POST)));
} }
@Test @Test
@@ -114,9 +127,18 @@ public class InspectorTests
assertTrue("SheepBlackSheepResource supports DELETE", metaData.supports(HttpMethod.DELETE)); assertTrue("SheepBlackSheepResource supports DELETE", metaData.supports(HttpMethod.DELETE));
params = metaData.getParameters(HttpMethod.DELETE); params = metaData.getParameters(HttpMethod.DELETE);
assertTrue("DELETE method on a relations should have 2 url params.", params.size() == 2); assertTrue("DELETE method on a relations should have 2 url params.", params.size() == 2);
metainfo = ResourceInspector.inspect(MultiPartTestRelationshipResource.class);
assertTrue("Must be one ResourceMetadata",metainfo.size()==1);
metaData = metainfo.get(0);
assertNotNull(metaData);
assertTrue("MultiPartTestRelationshipResource support POST", metaData.supports(HttpMethod.POST));
assertFalse("MultiPartTestRelationshipResource does not supports GET", metaData.supports(HttpMethod.GET));
assertFalse("MultiPartTestRelationshipResource does not supports PUT", metaData.supports(HttpMethod.PUT));
assertFalse("MultiPartTestRelationshipResource does not supports DELETE", metaData.supports(HttpMethod.DELETE));
assertTrue("MultiPartTestRelationshipResource must support MultiPartTestResponse", MultiPartTestResponse.class.equals(metaData.getObjectType(HttpMethod.POST)));
} }
@Test @Test
public void testInspectApi() public void testInspectApi()
{ {

View File

@@ -9,13 +9,18 @@ import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader; import java.io.StringReader;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.FileData;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.MultiPartRequest;
import org.alfresco.rest.framework.core.ResourceLocator; import org.alfresco.rest.framework.core.ResourceLocator;
import org.alfresco.rest.framework.core.ResourceMetadata; import org.alfresco.rest.framework.core.ResourceMetadata;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException; import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
@@ -30,12 +35,16 @@ import org.alfresco.rest.framework.webscripts.ResourceWebScriptDelete;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptGet; import org.alfresco.rest.framework.webscripts.ResourceWebScriptGet;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptPost; import org.alfresco.rest.framework.webscripts.ResourceWebScriptPost;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptPut; import org.alfresco.rest.framework.webscripts.ResourceWebScriptPut;
import org.alfresco.util.TempFileProvider;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.extensions.surf.util.Content; import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.Match; import org.springframework.extensions.webscripts.Match;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.servlet.FormData;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
/** /**
* Tests extracting of params from req * Tests extracting of params from req
@@ -119,7 +128,7 @@ public class ParamsExtractorTests
assertNotNull(params.getFilter()); assertNotNull(params.getFilter());
assertTrue("Default filter is BeanPropertiesFilter.AllProperties", BeanPropertiesFilter.AllProperties.class.equals(params.getFilter().getClass())); assertTrue("Default filter is BeanPropertiesFilter.AllProperties", BeanPropertiesFilter.AllProperties.class.equals(params.getFilter().getClass()));
Object passed = params.getPassedIn(); Object passed = params.getPassedIn();
assertNotNull(passed); assertNotNull(passed);
@@ -178,6 +187,65 @@ public class ParamsExtractorTests
} }
} }
@Test
public void testMultiPartPostExtractor() throws Exception
{
ResourceWebScriptPost extractor = new ResourceWebScriptPost();
extractor.setJsonHelper(jsonHelper);
Map<String, String> templateVars = new HashMap<String, String>();
WebScriptRequest request = mock(WebScriptRequest.class);
when(request.getServiceMatch()).thenReturn(new Match(null, templateVars, null));
File file = TempFileProvider.createTempFile("ParamsExtractorTests-", ".txt");
PrintWriter writer = new PrintWriter(file);
writer.println("Multipart Mock test.");
writer.close();
MultiPartRequest reqBody = MultiPartBuilder.create()
.setFileData(new FileData(file.getName(), file, MimetypeMap.MIMETYPE_TEXT_PLAIN))
.build();
MockHttpServletRequest mockRequest = new MockHttpServletRequest("POST", "");
mockRequest.setContent(reqBody.getBody());
mockRequest.setContentType(reqBody.getContentType());
when(request.getContentType()).thenReturn("multipart/form-data");
when(request.parseContent()).thenReturn(new FormData(mockRequest));
Params params = extractor.extractParams(mockEntity(), request);
assertNotNull(params);
Object passed = params.getPassedIn();
assertNotNull(passed);
assertTrue(FormData.class.isAssignableFrom(passed.getClass()));
FormData formData = (FormData) passed;
assertTrue(formData.getIsMultiPart());
assertNotNull(params.getStatus());
assertFalse(params.getStatus().getRedirect());
// No entity id for POST
templateVars.put(ResourceLocator.ENTITY_ID, "1234");
try
{
params = extractor.extractParams(mockEntity(), request);
fail("Should not get here. No entity id for POST");
}
catch (UnsupportedResourceOperationException uoe)
{
assertNotNull(uoe);
}
params = extractor.extractParams(mockRelationship(), request);
assertNotNull(params);
assertEquals("1234", params.getEntityId());
passed = params.getPassedIn();
assertNotNull(passed);
assertTrue(FormData.class.isAssignableFrom(passed.getClass()));
formData = (FormData) passed;
assertTrue(formData.getIsMultiPart());
}
@Test @Test
public void testPutExtractor() throws IOException public void testPutExtractor() throws IOException
{ {

View File

@@ -11,7 +11,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -19,8 +21,12 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.FileData;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.MultiPartRequest;
import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.core.ResourceDictionaryBuilder; import org.alfresco.rest.framework.core.ResourceDictionaryBuilder;
import org.alfresco.rest.framework.core.ResourceLookupDictionary; import org.alfresco.rest.framework.core.ResourceLookupDictionary;
@@ -34,6 +40,7 @@ import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.ActionExecutor.ExecutionCallback; import org.alfresco.rest.framework.resource.actions.ActionExecutor.ExecutionCallback;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction.Read; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction.Read;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction.ReadById; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction.ReadById;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
@@ -52,6 +59,7 @@ import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat;
import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript; import org.alfresco.rest.framework.webscripts.AbstractResourceWebScript;
import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper; import org.alfresco.rest.framework.webscripts.ResourceWebScriptHelper;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonGenerator;
@@ -69,7 +77,9 @@ import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.extensions.webscripts.Format; import org.springframework.extensions.webscripts.Format;
import org.springframework.extensions.webscripts.servlet.FormData;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -133,6 +143,31 @@ public class SerializeTests
out = writeResponse(helper.postProcessResponse(api,null, Params.valueOf("notUsed", null), resources)); out = writeResponse(helper.postProcessResponse(api,null, Params.valueOf("notUsed", null), resources));
assertTrue("There must be json output as List", StringUtils.startsWith(out, "{\"list\":")); assertTrue("There must be json output as List", StringUtils.startsWith(out, "{\"list\":"));
} }
@Test
public void testInvokeMultiPartEntity() throws IOException
{
ResourceWithMetadata entityResource = locator.locateEntityResource(api,"multiparttest", HttpMethod.POST);
assertNotNull(entityResource);
MultiPartResourceAction.Create<?> resource = (MultiPartResourceAction.Create<?>) entityResource.getResource();
File file = TempFileProvider.createTempFile("ParamsExtractorTests-", ".txt");
PrintWriter writer = new PrintWriter(file);
writer.println("Multipart Mock test2.");
writer.close();
MultiPartRequest reqBody = MultiPartBuilder.create()
.setFileData(new FileData(file.getName(), file, MimetypeMap.MIMETYPE_TEXT_PLAIN))
.build();
MockHttpServletRequest mockRequest = new MockHttpServletRequest("POST", "");
mockRequest.setContent(reqBody.getBody());
mockRequest.setContentType(reqBody.getContentType());
String out = writeResponse(helper.postProcessResponse(api,null, NOT_USED, resource.create(new FormData(mockRequest), NOT_USED)));
assertTrue("There must be json output", StringUtils.startsWith(out, "{\"entry\":"));
}
@Test @Test
public void testSerializeResponse() throws IOException public void testSerializeResponse() throws IOException
{ {

View File

@@ -59,7 +59,7 @@ public class WriterTests
ResourceDictionary resourceDic = locator.getDictionary(); ResourceDictionary resourceDic = locator.getDictionary();
Map<String, ResourceWithMetadata> apiResources = resourceDic.getAllResources().get(api); Map<String, ResourceWithMetadata> apiResources = resourceDic.getAllResources().get(api);
String writtenOut = testWriter(defaultMetaWriter, apiResources.get("/sheep"), apiResources); String writtenOut = testWriter(defaultMetaWriter, apiResources.get("/sheep"), apiResources);
assertTrue(writtenOut.startsWith("{\"list\":{\"pagination\":{\"count\":4")); assertTrue(writtenOut.startsWith("{\"list\":{\"pagination\":{\"count\":5"));
// ResourceMetaDataWriter wadlWriter = new WebScriptOptionsMetaData(); // ResourceMetaDataWriter wadlWriter = new WebScriptOptionsMetaData();
// writtenOut = testWriter(wadlWriter, apiResources.get("/sheep"), apiResources); // writtenOut = testWriter(wadlWriter, apiResources.get("/sheep"), apiResources);