From e971cfdaeabf55b1d24dd118d791263cae281238 Mon Sep 17 00:00:00 2001 From: "Brian M. Long" Date: Tue, 8 Mar 2022 13:36:39 -0500 Subject: [PATCH] fixed CXF createNode --- .../acs/api/NodeBodyCreateMultipart.java | 99 +++++++++++++++++++ .../alfresco/acs/api/NodesCxfApi.java | 48 ++++++--- .../alfresco/acs/api/NodesJerseyApi.java | 11 +-- .../alfresco/acs/ConnectionCxfClientIT.java | 34 ++++++- 4 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/inteligr8/alfresco/acs/api/NodeBodyCreateMultipart.java diff --git a/src/main/java/com/inteligr8/alfresco/acs/api/NodeBodyCreateMultipart.java b/src/main/java/com/inteligr8/alfresco/acs/api/NodeBodyCreateMultipart.java new file mode 100644 index 0000000..90236f8 --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/acs/api/NodeBodyCreateMultipart.java @@ -0,0 +1,99 @@ +package com.inteligr8.alfresco.acs.api; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; +import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.inteligr8.alfresco.acs.model.NodeBodyCreate; + +public class NodeBodyCreateMultipart extends MultipartBody { + + private static final ObjectMapper om = new ObjectMapper(); + + public static NodeBodyCreateMultipart from( + NodeBodyCreate nodeBody, String filename, InputStream istream, + Boolean autoRename, Boolean majorVersion, Boolean versioningEnabled) throws IOException { + List atts = new LinkedList<>(); + atts.addAll(toAttachments(nodeBody)); + if (autoRename != null) + atts.add(toAttachment("autoRename", String.valueOf(autoRename))); + if (majorVersion != null) + atts.add(toAttachment("majorVersion", String.valueOf(majorVersion))); + if (versioningEnabled != null) + atts.add(toAttachment("versioningEnabled", String.valueOf(versioningEnabled))); + atts.add(toAttachment(filename, istream)); + return new NodeBodyCreateMultipart(atts, true); + } + + public NodeBodyCreateMultipart(List atts) throws IOException { + super(atts); + } + + public NodeBodyCreateMultipart(List atts, boolean outbound) throws IOException { + super(atts, outbound); + } + + public NodeBodyCreate getBody() throws IOException { + if (!MediaType.APPLICATION_JSON_TYPE.equals(this.getRootAttachment().getContentType())) + throw new IllegalStateException(); + + InputStream istream = this.getRootAttachment().getDataHandler().getInputStream(); + try { + return om.readValue(istream, NodeBodyCreate.class); + } finally { + istream.close(); + } + } + + public Attachment getFiledataAttachment() { + return this.getAttachment("filedata"); + } + + + + @SuppressWarnings("unchecked") + private static List toAttachments(NodeBodyCreate nodeBody) throws IOException { + List atts = new LinkedList<>(); + atts.add(toAttachment("name", nodeBody.getName())); + atts.add(toAttachment("nodeType", nodeBody.getNodeType())); + if (nodeBody.getAspectNames() != null) + atts.add(toAttachment("aspectNames", nodeBody.getAspectNames())); + if (nodeBody.getProperties() != null) + atts.add(toAttachment("properties", (Map)nodeBody.getProperties())); + return atts; + } + + private static Attachment toAttachment(String name, String value) { + return new Attachment(name, new ByteArrayInputStream(value.getBytes()), new ContentDisposition("form-data; name=\"" + name + "\"")); + } + + private static Attachment toAttachment(String name, Collection c) throws JsonProcessingException { + return toJsonAttachment(name, c); + } + + private static Attachment toAttachment(String name, Map map) throws JsonProcessingException { + return toJsonAttachment(name, map); + } + + private static Attachment toJsonAttachment(String name, Object obj) throws JsonProcessingException { + String json = om.writeValueAsString(obj); + return new Attachment(name, new ByteArrayInputStream(json.getBytes()), new ContentDisposition("form-data; name=\"" + name + "\"")); + } + + private static Attachment toAttachment(String filename, InputStream istream) { + return new Attachment("filedata", istream, new ContentDisposition("form-data; name=\"filedata\"; filename=\"" + filename + "\"")); + } + +} diff --git a/src/main/java/com/inteligr8/alfresco/acs/api/NodesCxfApi.java b/src/main/java/com/inteligr8/alfresco/acs/api/NodesCxfApi.java index fcfd028..f4f69ea 100644 --- a/src/main/java/com/inteligr8/alfresco/acs/api/NodesCxfApi.java +++ b/src/main/java/com/inteligr8/alfresco/acs/api/NodesCxfApi.java @@ -1,20 +1,15 @@ package com.inteligr8.alfresco.acs.api; -import java.util.List; - import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import org.apache.cxf.jaxrs.ext.multipart.Attachment; -import org.apache.cxf.jaxrs.ext.multipart.Multipart; +import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import com.inteligr8.alfresco.acs.model.Error; -import com.inteligr8.alfresco.acs.model.NodeBodyCreate; import com.inteligr8.alfresco.acs.model.NodeEntry; import io.swagger.annotations.Api; @@ -31,7 +26,7 @@ public interface NodesCxfApi { @Consumes({ MediaType.MULTIPART_FORM_DATA }) @Produces({ MediaType.APPLICATION_JSON }) @ApiOperation(value = "Create a node", tags={ }) - @ApiResponses(value = { + @ApiResponses(value = { @ApiResponse(code = 201, message = "Successful response", response = NodeEntry.class), @ApiResponse(code = 400, message = "Invalid parameter: **nodeId** is not a valid format or **nodeBodyCreate** is invalid "), @ApiResponse(code = 401, message = "Authentication failed"), @@ -45,12 +40,35 @@ public interface NodesCxfApi { @ApiResponse(code = 200, message = "Unexpected error", response = Error.class) }) public NodeEntry createNode( @PathParam("nodeId") String nodeId, - @Multipart NodeBodyCreate nodeBodyCreate, - @QueryParam("autoRename") Boolean autoRename, - @QueryParam("majorVersion") Boolean majorVersion, - @QueryParam("versioningEnabled") Boolean versioningEnabled, - @QueryParam("include") List include, - @QueryParam("fields") List fields, - @Multipart(value = "filedata", required = false) Attachment attachment); - + MultipartBody body); +/* + * This better impl doesn't work + * + @POST + @Path("/nodes/{nodeId}/children") + @Consumes({ MediaType.MULTIPART_FORM_DATA }) + @Produces({ MediaType.APPLICATION_JSON }) + @ApiOperation(value = "Create a node", tags={ }) + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Successful response", response = NodeEntry.class), + @ApiResponse(code = 400, message = "Invalid parameter: **nodeId** is not a valid format or **nodeBodyCreate** is invalid "), + @ApiResponse(code = 401, message = "Authentication failed"), + @ApiResponse(code = 403, message = "Current user does not have permission to create children of **nodeId**"), + @ApiResponse(code = 404, message = "**nodeId** or **renditionId** does not exist "), + @ApiResponse(code = 409, message = "New name clashes with an existing node in the current parent folder"), + @ApiResponse(code = 413, message = "Content exceeds individual file size limit configured for the network or system"), + @ApiResponse(code = 415, message = "Content Type is not supported"), + @ApiResponse(code = 422, message = "Model integrity exception including a file name containing invalid characters"), + @ApiResponse(code = 507, message = "Content exceeds overall storage quota limit configured for the network or system"), + @ApiResponse(code = 200, message = "Unexpected error", response = Error.class) }) + public NodeEntry createNode( + @PathParam("nodeId") String nodeId, + @Multipart("name") String name, + @Multipart("nodeType") String nodeType, + @Multipart("filedata") InputStream fileStream, + @Multipart("filedata") ContentDisposition disposition, + @Multipart(value = "autoRename", required = false) Boolean autoRename, + @Multipart(value = "majorVersion", required = false) Boolean majorVersion, + @Multipart(value = "versioningEnabled", required = false) Boolean versioningEnabled); +*/ } diff --git a/src/main/java/com/inteligr8/alfresco/acs/api/NodesJerseyApi.java b/src/main/java/com/inteligr8/alfresco/acs/api/NodesJerseyApi.java index 6bbccfb..d5d59b2 100644 --- a/src/main/java/com/inteligr8/alfresco/acs/api/NodesJerseyApi.java +++ b/src/main/java/com/inteligr8/alfresco/acs/api/NodesJerseyApi.java @@ -8,7 +8,6 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; @@ -47,11 +46,11 @@ public interface NodesJerseyApi { public NodeEntry createNode( @PathParam("nodeId") String nodeId, NodeBodyCreate nodeBodyCreate, - @QueryParam("autoRename") Boolean autoRename, - @QueryParam("majorVersion") Boolean majorVersion, - @QueryParam("versioningEnabled") Boolean versioningEnabled, - @QueryParam("include") List include, - @QueryParam("fields") List fields, + @FormDataParam("autoRename") Boolean autoRename, + @FormDataParam("majorVersion") Boolean majorVersion, + @FormDataParam("versioningEnabled") Boolean versioningEnabled, + @FormDataParam("include") List include, + @FormDataParam("fields") List fields, @FormDataParam("filedata") InputStream filedataStream, @FormDataParam("filedata") FormDataContentDisposition filedataDisposition); diff --git a/src/test/java/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java b/src/test/java/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java index 4b979c1..31379e7 100644 --- a/src/test/java/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java +++ b/src/test/java/com/inteligr8/alfresco/acs/ConnectionCxfClientIT.java @@ -1,10 +1,21 @@ package com.inteligr8.alfresco.acs; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import com.inteligr8.alfresco.acs.api.NodeBodyCreateMultipart; +import com.inteligr8.alfresco.acs.model.NodeBodyCreate; +import com.inteligr8.alfresco.acs.model.RequestQuery; +import com.inteligr8.alfresco.acs.model.RequestQuery.LanguageEnum; +import com.inteligr8.alfresco.acs.model.ResultNode; +import com.inteligr8.alfresco.acs.model.ResultSetPaging; +import com.inteligr8.alfresco.acs.model.SearchRequest; import com.inteligr8.rs.ClientConfiguration; @TestPropertySource(locations = {"/local.properties"}) @@ -13,7 +24,7 @@ public class ConnectionCxfClientIT extends ConnectionClientIT { @Autowired @Qualifier("acs.api.cxf") - private AcsPublicRestApi client; + private AcsPublicRestApiCxfImpl client; @Override public AcsPublicRestApi getClient() { @@ -25,4 +36,25 @@ public class ConnectionCxfClientIT extends ConnectionClientIT { return this.client.getConfig(); } + @Test + public void uploadFile() throws IOException { + RequestQuery query = new RequestQuery(); + query.setLanguage(LanguageEnum.AFTS); + query.setQuery("=@cm:name:'Shared'"); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.setQuery(query); + + ResultSetPaging paging = this.client.getSearchApi().search(searchRequest); + ResultNode folderNode = paging.getList().getEntries().iterator().next().getEntry(); + String folderNodeId = folderNode.getId(); + + NodeBodyCreate nodeBody = new NodeBodyCreate().nodeType("cm:content").name("TestFolder"); + + ByteArrayInputStream istream = new ByteArrayInputStream("This is a test".getBytes()); + NodeBodyCreateMultipart body = NodeBodyCreateMultipart.from(nodeBody, "test.txt", istream, true, null, null); + + this.client.getNodesExtApi().createNode(folderNodeId, body); + } + }