Repository: olingo-odata4 Updated Branches: refs/heads/master fe8df96fe -> e4a4f9e6e
Moving contained CRUD tests from external test service to fit Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/e4a4f9e6 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/e4a4f9e6 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/e4a4f9e6 Branch: refs/heads/master Commit: e4a4f9e6eb85ba182084088e67029dc2b833939a Parents: fe8df96 Author: Francesco Chicchiriccò <[email protected]> Authored: Fri Apr 18 11:05:37 2014 +0200 Committer: Francesco Chicchiriccò <[email protected]> Committed: Fri Apr 18 11:05:37 2014 +0200 ---------------------------------------------------------------------- .../org/apache/olingo/fit/AbstractServices.java | 58 ++--- .../apache/olingo/fit/V3ActionOverloading.java | 4 + .../java/org/apache/olingo/fit/V4OpenType.java | 5 +- .../java/org/apache/olingo/fit/V4Services.java | 215 +++++++++++++++++-- .../olingo/fit/utils/AbstractUtilities.java | 5 +- .../org/apache/olingo/fit/utils/Commons.java | 1 + .../org/apache/olingo/fit/utils/FSManager.java | 15 +- .../core/it/v4/EntityCreateTestITCase.java | 39 +--- .../core/it/v4/EntityRetrieveTestITCase.java | 10 +- .../edm/primitivetype/EdmDateTimeOffset.java | 29 ++- .../primitivetype/EdmDateTimeOffsetTest.java | 9 + 11 files changed, 271 insertions(+), 119 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java index 9f992e9..b624ebe 100644 --- a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java +++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java @@ -122,17 +122,27 @@ public abstract class AbstractServices { protected final ODataServiceVersion version; + protected final FITAtomDeserializer atomDeserializer; + + protected final AtomSerializer atomSerializer; + + protected final ObjectMapper mapper; + + protected final DataBinder dataBinder; + protected final AbstractXMLUtilities xml; protected final AbstractJSONUtilities json; - @Context - protected UriInfo uriInfo; - protected Metadata metadata; public AbstractServices(final ODataServiceVersion version) throws Exception { this.version = version; + this.atomDeserializer = Commons.getAtomDeserializer(version); + this.atomSerializer = Commons.getAtomSerializer(version); + this.mapper = Commons.getJsonMapper(version); + this.dataBinder = new DataBinder(version); + if (version.compareTo(ODataServiceVersion.V30) <= 0) { this.xml = new org.apache.olingo.fit.utils.v3.XMLUtilities(); this.json = new org.apache.olingo.fit.utils.v3.JSONUtilities(); @@ -453,10 +463,6 @@ public abstract class AbstractServices { throw new ConcurrentModificationException("Concurrent modification"); } - final ObjectMapper mapper = Commons.getJsonMapper(version); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); - final Accept contentTypeValue = Accept.parse(contentType, version); final AtomEntryImpl entryChanges; @@ -471,7 +477,7 @@ public abstract class AbstractServices { mapper.readValue(IOUtils.toInputStream(changes), new TypeReference<JSONEntryImpl>() { }); - entryChanges = (new DataBinder(version)).getAtomEntry(jcont.getObject()); + entryChanges = dataBinder.getAtomEntry(jcont.getObject()); } final Container<AtomEntryImpl> container = atomDeserializer.read(entityInfo.getValue(), AtomEntryImpl.class); @@ -554,9 +560,6 @@ public abstract class AbstractServices { getUtilities(acceptType).readEntry(acceptType, IOUtils.toInputStream(entity))); } - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); - final ObjectMapper mapper = Commons.getJsonMapper(version); - final Container<AtomEntryImpl> cres; if (acceptType == Accept.ATOM) { cres = atomDeserializer.read(res, AtomEntryImpl.class); @@ -564,7 +567,7 @@ public abstract class AbstractServices { final Container<JSONEntryImpl> jcont = mapper.readValue(res, new TypeReference<JSONEntryImpl>() { }); cres = new Container<AtomEntryImpl>(jcont.getContextURL(), jcont.getMetadataETag(), - (new DataBinder(version)).getAtomEntry(jcont.getObject())); + dataBinder.getAtomEntry(jcont.getObject())); } final String path = Commons.getEntityBasePath(entitySetName, entityId); @@ -615,17 +618,12 @@ public abstract class AbstractServices { AbstractUtilities utils = xml; try { final Accept acceptType = Accept.parse(accept, version); - if (acceptType == Accept.XML || acceptType == Accept.TEXT) { throw new UnsupportedMediaTypeException("Unsupported media type"); } utils = getUtilities(acceptType); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); - final ObjectMapper mapper = Commons.getJsonMapper(version); - final Container<AtomEntryImpl> container; final EntitySet entitySet = getMetadataObj().getEntitySet(entitySetName); @@ -670,7 +668,7 @@ public abstract class AbstractServices { mapper.readValue(IOUtils.toInputStream(entity), new TypeReference<JSONEntryImpl>() { }); - entry = new DataBinder(version).getAtomEntry(jcontainer.getObject()); + entry = dataBinder.getAtomEntry(jcontainer.getObject()); container = new Container<AtomEntryImpl>( jcontainer.getContextURL(), @@ -933,8 +931,6 @@ public abstract class AbstractServices { final InputStream feed = FSManager.instance(version).readFile(builder.toString(), Accept.ATOM); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); final Container<AtomFeedImpl> container = atomDeserializer.read(feed, AtomFeedImpl.class); setInlineCount(container.getObject(), count); @@ -947,11 +943,9 @@ public abstract class AbstractServices { writer.flush(); writer.close(); } else { - final ObjectMapper mapper = Commons.getJsonMapper(version); - mapper.writeValue( writer, new JsonFeedContainer<JSONFeedImpl>(container.getContextURL(), container.getMetadataETag(), - new DataBinder(version).getJsonFeed(container.getObject()))); + dataBinder.getJsonFeed(container.getObject()))); } return xml.createResponse( @@ -1058,9 +1052,6 @@ public abstract class AbstractServices { final InputStream entity = entityInfo.getValue(); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); - Container<AtomEntryImpl> container = atomDeserializer.read(entity, AtomEntryImpl.class); if (container.getContextURL() == null) { container = new Container<AtomEntryImpl>(URI.create(Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) @@ -1375,7 +1366,7 @@ public abstract class AbstractServices { final AbstractUtilities utils = getUtilities(null); - InputStream res = utils.putMediaInMemory(entitySetName, entityId, IOUtils.toInputStream(value)); + final InputStream res = utils.putMediaInMemory(entitySetName, entityId, IOUtils.toInputStream(value)); final String location = uriInfo.getRequestUri().toASCIIString().replace("/$value", ""); @@ -1551,10 +1542,9 @@ public abstract class AbstractServices { } try { - LinkInfo linkInfo = xml.readLinks(entitySetName, entityId, path, Accept.XML); + final LinkInfo linkInfo = xml.readLinks(entitySetName, entityId, path, Accept.XML); final Map.Entry<String, List<String>> links = xml.extractLinkURIs(linkInfo.getLinks()); - InputStream stream = xml.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed()); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); + final InputStream stream = xml.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed()); final ByteArrayOutputStream content = new ByteArrayOutputStream(); final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING); @@ -1563,32 +1553,28 @@ public abstract class AbstractServices { final Container<Feed> container = atomDeserializer.<Feed, AtomFeedImpl>read(stream, AtomFeedImpl.class); if (acceptType == Accept.ATOM) { - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); atomSerializer.write(writer, container); writer.flush(); writer.close(); } else { - final ObjectMapper mapper = Commons.getJsonMapper(version); mapper.writeValue( writer, new JsonFeedContainer<JSONFeedImpl>(container.getContextURL(), container.getMetadataETag(), - (new DataBinder(version)).getJsonFeed((AtomFeedImpl) container.getObject()))); + dataBinder.getJsonFeed((AtomFeedImpl) container.getObject()))); } } else { final Container<Entry> container = atomDeserializer.<Entry, AtomEntryImpl>read(stream, AtomEntryImpl.class); if (acceptType == Accept.ATOM) { - final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); atomSerializer.write(writer, container); writer.flush(); writer.close(); } else { - final ObjectMapper mapper = Commons.getJsonMapper(version); mapper.writeValue( writer, new JsonEntryContainer<JSONEntryImpl>(container.getContextURL(), container.getMetadataETag(), - (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject()))); + dataBinder.getJsonEntry((AtomEntryImpl) container.getObject()))); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/V3ActionOverloading.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/V3ActionOverloading.java b/fit/src/main/java/org/apache/olingo/fit/V3ActionOverloading.java index d26daa5..a070527 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V3ActionOverloading.java +++ b/fit/src/main/java/org/apache/olingo/fit/V3ActionOverloading.java @@ -30,9 +30,11 @@ 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.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import static javax.ws.rs.core.Response.status; +import javax.ws.rs.core.UriInfo; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.olingo.commons.api.data.Feed; @@ -117,6 +119,7 @@ public class V3ActionOverloading extends AbstractServices { @GET @Path("/Product({entityId})") public Response getProduct( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept, @PathParam("entityId") final String entityId, @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) { @@ -158,6 +161,7 @@ public class V3ActionOverloading extends AbstractServices { @GET @Path("/OrderLine(OrderId={orderId},ProductId={productId})") public Response getOrderLine( + @Context UriInfo uriInfo, @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept, @PathParam("orderId") final String orderId, @PathParam("productId") final String productId, http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java b/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java index aff2f43..748a614 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java +++ b/fit/src/main/java/org/apache/olingo/fit/V4OpenType.java @@ -56,7 +56,7 @@ public class V4OpenType { public V4OpenType() throws Exception { this.openMetadata = new Metadata(FSManager.instance(ODataServiceVersion.V40). readFile("openType" + StringUtils.capitalize(Constants.get(ODataServiceVersion.V40, ConstantKey.METADATA)), - Accept.XML)); + Accept.XML)); this.services = new V4Services() { @Override protected Metadata getMetadataObj() { @@ -115,8 +115,7 @@ public class V4OpenType { @QueryParam("$expand") @DefaultValue(StringUtils.EMPTY) String expand, @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) { - return replaceServiceName( - services.getEntityInternal( + return replaceServiceName(services.getEntityInternal( uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, entityId, format, expand, select, false)); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/V4Services.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/V4Services.java b/fit/src/main/java/org/apache/olingo/fit/V4Services.java index 8de437b..c6de7fe 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V4Services.java +++ b/fit/src/main/java/org/apache/olingo/fit/V4Services.java @@ -20,16 +20,22 @@ package org.apache.olingo.fit; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.InputStream; +import java.io.OutputStreamWriter; import java.util.Map; +import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.NotFoundException; -import org.apache.olingo.fit.utils.XHTTPMethodInterceptor; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.commons.codec.binary.Base64; @@ -41,10 +47,14 @@ import org.apache.olingo.commons.api.data.Feed; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; import org.apache.olingo.commons.core.data.AtomEntryImpl; +import org.apache.olingo.commons.core.data.AtomFeedImpl; +import org.apache.olingo.commons.core.data.AtomSerializer; import org.apache.olingo.commons.core.data.JSONEntryImpl; +import org.apache.olingo.commons.core.data.JSONFeedImpl; import org.apache.olingo.commons.core.edm.EdmTypeInfo; import org.apache.olingo.fit.methods.PATCH; import org.apache.olingo.fit.serializer.FITAtomDeserializer; +import org.apache.olingo.fit.serializer.JsonFeedContainer; import org.apache.olingo.fit.utils.AbstractUtilities; import org.apache.olingo.fit.utils.Accept; import org.apache.olingo.fit.utils.Commons; @@ -54,6 +64,7 @@ import org.apache.olingo.fit.utils.DataBinder; import org.apache.olingo.fit.utils.FSManager; import org.apache.olingo.fit.utils.LinkInfo; import org.apache.olingo.fit.utils.ResolvingReferencesInterceptor; +import org.apache.olingo.fit.utils.XHTTPMethodInterceptor; import org.springframework.stereotype.Service; @Service @@ -98,7 +109,7 @@ public class V4Services extends AbstractServices { return utils.getValue().createResponse( FSManager.instance(version).readFile(Constants.get(version, ConstantKey.REF) - + File.separatorChar + filename, utils.getKey()), + + File.separatorChar + filename, utils.getKey()), null, utils.getKey()); } catch (Exception e) { @@ -120,7 +131,7 @@ public class V4Services extends AbstractServices { final Response response = getEntityInternal(uriInfo.getRequestUri().toASCIIString(), - accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); + accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY, false); return response.getStatus() >= 400 ? postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, changes) : super.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); @@ -144,12 +155,18 @@ public class V4Services extends AbstractServices { return postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entityId); } } + + private StringBuilder containedPath(final String entityId, final String containedEntitySetName) { + return new StringBuilder("Accounts").append(File.separatorChar). + append(entityId).append(File.separatorChar). + append("links").append(File.separatorChar). + append(containedEntitySetName); + } @GET - @Path("/{entitySetName}({entityId})/{containedEntitySetName}({containedEntityId})") + @Path("/Accounts({entityId})/{containedEntitySetName}({containedEntityId})") public Response getContainedEntity( @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, - @PathParam("entitySetName") String entitySetName, @PathParam("entityId") String entityId, @PathParam("containedEntitySetName") String containedEntitySetName, @PathParam("containedEntityId") String containedEntityId, @@ -162,23 +179,106 @@ public class V4Services extends AbstractServices { } else { acceptType = Accept.parse(accept, version); } - if (acceptType == Accept.XML || acceptType == Accept.TEXT) { throw new UnsupportedMediaTypeException("Unsupported media type"); } - final LinkInfo links = xml.readLinks( - entitySetName, entityId, containedEntitySetName + "(" + containedEntityId + ")", acceptType); + final InputStream entry = FSManager.instance(version). + readFile(containedPath(entityId, containedEntitySetName). + append('(').append(containedEntityId).append(')').toString(), Accept.ATOM); + + final Container<AtomEntryImpl> container = atomDeserializer.read(entry, AtomEntryImpl.class); return xml.createResponse( - links.getLinks(), - links.getEtag(), + null, + xml.writeEntry(acceptType, container), + null, acceptType); } catch (Exception e) { return xml.createFaultResponse(accept, e); } } + @POST + @Path("/Accounts({entityId})/{containedEntitySetName:.*}") + public Response postContainedEntity( + @Context UriInfo uriInfo, + @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, + @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType, + @PathParam("entityId") String entityId, + @PathParam("containedEntitySetName") String containedEntitySetName, + final String entity) { + + // default + try { + final Accept acceptType = Accept.parse(accept, version); + if (acceptType == Accept.XML || acceptType == Accept.TEXT) { + throw new UnsupportedMediaTypeException("Unsupported media type"); + } + + final AbstractUtilities utils = getUtilities(acceptType); + + // 1. parse the entry (from Atom or JSON) into AtomEntryImpl + final Container<AtomEntryImpl> entryContainer; + final AtomEntryImpl entry; + final Accept contentTypeValue = Accept.parse(contentType, version); + if (Accept.ATOM == contentTypeValue) { + entryContainer = atomDeserializer.read(IOUtils.toInputStream(entity), AtomEntryImpl.class); + entry = entryContainer.getObject(); + } else { + final Container<JSONEntryImpl> jcontainer = + mapper.readValue(IOUtils.toInputStream(entity), new TypeReference<JSONEntryImpl>() { + }); + + entry = dataBinder.getAtomEntry(jcontainer.getObject()); + + entryContainer = new Container<AtomEntryImpl>( + jcontainer.getContextURL(), + jcontainer.getMetadataETag(), + entry); + } + + final EdmTypeInfo contained = new EdmTypeInfo.Builder().setTypeExpression(getMetadataObj(). + getNavigationProperties("Accounts").get(containedEntitySetName).getType()).build(); + final String entityKey = getUtilities(contentTypeValue). + getDefaultEntryKey(contained.getFullQualifiedName().getName(), entry); + + // 2. Store the new entity + final String atomEntryRelativePath = containedPath(entityId, containedEntitySetName). + append('(').append(entityKey).append(')').toString(); + FSManager.instance(version).putInMemory( + utils.writeEntry(Accept.ATOM, entryContainer), + FSManager.instance(version).getAbsolutePath(atomEntryRelativePath, Accept.ATOM)); + + // 3. Update the contained entity set + final String atomFeedRelativePath = containedPath(entityId, containedEntitySetName).toString(); + final InputStream feedIS = FSManager.instance(version).readFile(atomFeedRelativePath, Accept.ATOM); + final Container<AtomFeedImpl> feedContainer = atomDeserializer.read(feedIS, AtomFeedImpl.class); + feedContainer.getObject().getEntries().add(entry); + + final ByteArrayOutputStream content = new ByteArrayOutputStream(); + final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING); + atomSerializer.write(writer, feedContainer); + writer.flush(); + writer.close(); + + FSManager.instance(version).putInMemory( + new ByteArrayInputStream(content.toByteArray()), + FSManager.instance(version).getAbsolutePath(atomFeedRelativePath, Accept.ATOM)); + + // Finally, return + return utils.createResponse( + uriInfo.getRequestUri().toASCIIString() + "(" + entityKey + ")", + utils.writeEntry(acceptType, entryContainer), + null, + acceptType, + Response.Status.CREATED); + } catch (Exception e) { + LOG.error("While creating new contained entity", e); + return xml.createFaultResponse(accept, e); + } + } + @PATCH @Path("/{entitySetName}({entityId})/{containedEntitySetName}({containedEntityId})") public Response patchContainedEntity( @@ -211,7 +311,6 @@ public class V4Services extends AbstractServices { final LinkInfo links = xml.readLinks( entitySetName, entityId, containedEntitySetName + "(" + containedEntityId + ")", Accept.ATOM); - final FITAtomDeserializer atomDeserializer = Commons.getAtomDeserializer(version); Container<AtomEntryImpl> container = atomDeserializer.read(links.getLinks(), AtomEntryImpl.class); final AtomEntryImpl original = container.getObject(); @@ -225,12 +324,9 @@ public class V4Services extends AbstractServices { getNavigationProperty(containedEntitySetName).getType(); final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(containedType).build(); - final ObjectMapper mapper = Commons.getJsonMapper(version); - final DataBinder dataBinder = new DataBinder(version); - final Container<JSONEntryImpl> jsonContainer = mapper.readValue(IOUtils.toInputStream(changes), new TypeReference<JSONEntryImpl>() { - }); + }); jsonContainer.getObject().setType(typeInfo.getFullQualifiedName().toString()); entryChanges = dataBinder.getAtomEntry(jsonContainer.getObject()); } @@ -251,4 +347,93 @@ public class V4Services extends AbstractServices { return xml.createFaultResponse(accept, e); } } + + @DELETE + @Path("/Accounts({entityId})/{containedEntitySetName}({containedEntityId})") + public Response removeContainedEntity( + @PathParam("entityId") String entityId, + @PathParam("containedEntitySetName") String containedEntitySetName, + @PathParam("containedEntityId") String containedEntityId) { + + try { + // 1. Fetch the contained entity to be removed + final InputStream entry = FSManager.instance(version). + readFile(containedPath(entityId, containedEntitySetName). + append('(').append(containedEntityId).append(')').toString(), Accept.ATOM); + final Container<AtomEntryImpl> container = atomDeserializer.read(entry, AtomEntryImpl.class); + + // 2. Remove the contained entity + final String atomEntryRelativePath = containedPath(entityId, containedEntitySetName). + append('(').append(containedEntityId).append(')').toString(); + FSManager.instance(version).deleteFile(atomEntryRelativePath); + + // 3. Update the contained entity set + final String atomFeedRelativePath = containedPath(entityId, containedEntitySetName).toString(); + final InputStream feedIS = FSManager.instance(version).readFile(atomFeedRelativePath, Accept.ATOM); + final Container<AtomFeedImpl> feedContainer = atomDeserializer.read(feedIS, AtomFeedImpl.class); + feedContainer.getObject().getEntries().remove(container.getObject()); + + final ByteArrayOutputStream content = new ByteArrayOutputStream(); + final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING); + atomSerializer.write(writer, feedContainer); + writer.flush(); + writer.close(); + + FSManager.instance(version).putInMemory( + new ByteArrayInputStream(content.toByteArray()), + FSManager.instance(version).getAbsolutePath(atomFeedRelativePath, Accept.ATOM)); + + return xml.createResponse(null, null, null, null, Response.Status.NO_CONTENT); + } catch (Exception e) { + return xml.createFaultResponse(Accept.XML.toString(version), e); + } + } + + @GET + @Path("/Accounts({entityId})/{containedEntitySetName:.*}") + public Response getContainedEntitySet( + @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept, + @PathParam("entityId") String entityId, + @PathParam("containedEntitySetName") String containedEntitySetName, + @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) { + + try { + final Accept acceptType; + if (StringUtils.isNotBlank(format)) { + acceptType = Accept.valueOf(format.toUpperCase()); + } else { + acceptType = Accept.parse(accept, version); + } + if (acceptType == Accept.XML || acceptType == Accept.TEXT) { + throw new UnsupportedMediaTypeException("Unsupported media type"); + } + + final InputStream feed = FSManager.instance(version). + readFile(containedPath(entityId, containedEntitySetName).toString(), Accept.ATOM); + + final Container<AtomFeedImpl> container = atomDeserializer.read(feed, AtomFeedImpl.class); + + final ByteArrayOutputStream content = new ByteArrayOutputStream(); + final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING); + + if (acceptType == Accept.ATOM) { + atomSerializer.write(writer, container); + writer.flush(); + writer.close(); + } else { + mapper.writeValue( + writer, new JsonFeedContainer<JSONFeedImpl>(container.getContextURL(), container.getMetadataETag(), + dataBinder.getJsonFeed(container.getObject()))); + } + + return xml.createResponse( + null, + new ByteArrayInputStream(content.toByteArray()), + null, + acceptType); + } catch (Exception e) { + return xml.createFaultResponse(accept, e); + } + } + } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java index 22a7f5b..a84ef60 100644 --- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java +++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java @@ -120,7 +120,6 @@ public abstract class AbstractUtilities { * Retrieve entity links and inlines. * * @param entitySetName - * @param entityKey * @param is * @return * @throws IOException @@ -589,7 +588,7 @@ public abstract class AbstractUtilities { final ObjectMapper mapper = Commons.getJsonMapper(version); mapper.writeValue( writer, new JsonEntryContainer<JSONEntryImpl>(container.getContextURL(), container.getMetadataETag(), - (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject()))); + new DataBinder(version).getJsonEntry((AtomEntryImpl) container.getObject()))); } return new ByteArrayInputStream(content.toByteArray()); @@ -672,6 +671,8 @@ public abstract class AbstractUtilities { } Commons.SEQUENCE.put(entitySetName, productDetailId); Commons.SEQUENCE.put("Products", productId); + } else if ("PaymentInstrument".equals(entitySetName)) { + res = getDefaultEntryKey(entitySetName, entry, "PaymentInstrumentID"); } else { throw new Exception(String.format("EntitySet '%s' not found", entitySetName)); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java index 2844676..c3c0cbb 100644 --- a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java +++ b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java @@ -89,6 +89,7 @@ public abstract class Commons { SEQUENCE.put("RowIndex", 1000); SEQUENCE.put("Products", 1000); SEQUENCE.put("ProductDetails", 1000); + SEQUENCE.put("PaymentInstrument", 10192); MEDIA_CONTENT.put("CustomerInfo", "CustomerinfoId"); MEDIA_CONTENT.put("Car", "VIN"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java ---------------------------------------------------------------------- diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java b/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java index 46654fe..b7d21f4 100644 --- a/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java +++ b/fit/src/main/java/org/apache/olingo/fit/utils/FSManager.java @@ -102,10 +102,7 @@ public class FSManager { public void putInMemory(final Container<AtomEntryImpl> container, final String relativePath) throws IOException { try { - final ODataServiceVersion serviceVersion = - version == ODataServiceVersion.V30 ? ODataServiceVersion.V30 : ODataServiceVersion.V40; - - final AtomSerializer atomSerializer = Commons.getAtomSerializer(serviceVersion); + final AtomSerializer atomSerializer = Commons.getAtomSerializer(version); final ByteArrayOutputStream content = new ByteArrayOutputStream(); final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING); @@ -116,13 +113,12 @@ public class FSManager { putInMemory(new ByteArrayInputStream(content.toByteArray()), getAbsolutePath(relativePath, Accept.ATOM)); content.reset(); - final ObjectMapper mapper = Commons.getJsonMapper(serviceVersion); + final ObjectMapper mapper = Commons.getJsonMapper(version); mapper.writeValue( writer, new JsonEntryContainer<JSONEntryImpl>( - container.getContextURL(), - container.getMetadataETag(), - (new DataBinder(version == ODataServiceVersion.V30 ? ODataServiceVersion.V30 : ODataServiceVersion.V40)). - getJsonEntry(container.getObject()))); + container.getContextURL(), + container.getMetadataETag(), + new DataBinder(version).getJsonEntry(container.getObject()))); putInMemory(new ByteArrayInputStream(content.toByteArray()), getAbsolutePath(relativePath, Accept.JSON_FULLMETA)); } catch (Exception e) { @@ -164,7 +160,6 @@ public class FSManager { } public void deleteFile(final String relativePath) { - for (Accept accept : Accept.values()) { final String path = getAbsolutePath(relativePath, accept); LOG.info("Delete {}", path); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityCreateTestITCase.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityCreateTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityCreateTestITCase.java index 6a736c6..6101041 100644 --- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityCreateTestITCase.java +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityCreateTestITCase.java @@ -21,12 +21,8 @@ package org.apache.olingo.client.core.it.v4; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.URI; import java.util.Calendar; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomUtils; import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest; import org.apache.olingo.client.api.communication.response.ODataDeleteResponse; @@ -43,31 +39,10 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.format.ODataPubFormat; import org.apache.olingo.commons.core.domain.v4.ODataEntityImpl; -import org.junit.Assume; -import org.junit.BeforeClass; import org.junit.Test; public class EntityCreateTestITCase extends AbstractTestITCase { - private static final String serviceRoot = "http://odatae2etest.azurewebsites.net/javatest/DefaultService"; - - // TODO: remove once fit provides contained entity CRUD - @BeforeClass - public static void checkServerIsOnline() throws IOException { - final Socket socket = new Socket(); - boolean reachable = false; - try { - socket.connect(new InetSocketAddress("odatae2etest.azurewebsites.net", 80), 2000); - reachable = true; - } catch (Exception e) { - LOG.warn("External test service not reachable, ignoring this whole class: {}", - OperationImportInvokeTestITCase.class.getName()); - } finally { - IOUtils.closeQuietly(socket); - } - Assume.assumeTrue(reachable); - } - private void order(final ODataPubFormat format, final int id) { final ODataEntity order = new ODataEntityImpl( new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Order")); @@ -116,8 +91,8 @@ public class EntityCreateTestITCase extends AbstractTestITCase { } private void onContained(final ODataPubFormat format) { - final URI uri = getClient().getURIBuilder(serviceRoot).appendEntitySetSegment("Accounts").appendKeySegment(101). - appendNavigationSegment("MyPaymentInstruments").build(); + final URI uri = getClient().getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Accounts"). + appendKeySegment(101).appendNavigationSegment("MyPaymentInstruments").build(); // 1. read contained collection before any operation ODataEntitySet instruments = getClient().getRetrieveRequestFactory().getEntitySetRequest(uri).execute().getBody(); @@ -171,8 +146,9 @@ public class EntityCreateTestITCase extends AbstractTestITCase { onContained(ODataPubFormat.JSON); } - private void deepInsert(final ODataPubFormat format) throws EdmPrimitiveTypeException { - final int productId = RandomUtils.nextInt(10, 20); + private void deepInsert(final ODataPubFormat format, final int productId, final int productDetailId) + throws EdmPrimitiveTypeException { + final ODataEntity product = getClient().getObjectFactory(). newEntity(new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Product")); product.getProperties().add(getClient().getObjectFactory().newPrimitiveProperty("ProductID", @@ -201,7 +177,6 @@ public class EntityCreateTestITCase extends AbstractTestITCase { product.getProperty("CoverColors").getCollectionValue().add(getClient().getObjectFactory(). newEnumValue("Microsoft.Test.OData.Services.ODataWCFService.Color", "Red")); - final int productDetailId = RandomUtils.nextInt(10, 20); final ODataEntity detail = getClient().getObjectFactory(). newEntity(new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.ProductDetail")); detail.getProperties().add(getClient().getObjectFactory().newPrimitiveProperty("ProductID", @@ -242,11 +217,11 @@ public class EntityCreateTestITCase extends AbstractTestITCase { @Test public void atomDeepInsert() throws EdmPrimitiveTypeException { - deepInsert(ODataPubFormat.ATOM); + deepInsert(ODataPubFormat.ATOM, 10, 10); } @Test public void jsonDeepInsert() throws EdmPrimitiveTypeException { - deepInsert(ODataPubFormat.JSON_FULL_METADATA); + deepInsert(ODataPubFormat.JSON_FULL_METADATA, 11, 11); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java index 2917bc4..9e0b390 100644 --- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java @@ -248,16 +248,16 @@ public class EntityRetrieveTestITCase extends AbstractTestITCase { } @Test - public void retrieveEntityViaReferenceAsAtom() { - retrieveEntityViaReference(ODataPubFormat.ATOM); + public void atomReference() { + reference(ODataPubFormat.ATOM); } @Test - public void retrieveEntityViaReferenceAsJSON() { - retrieveEntityViaReference(ODataPubFormat.JSON_FULL_METADATA); + public void jsonReference() { + reference(ODataPubFormat.JSON_FULL_METADATA); } - private void retrieveEntityViaReference(final ODataPubFormat format) { + private void reference(final ODataPubFormat format) { final URIBuilder uriBuilder = client.getURIBuilder(getServiceRoot()). appendEntitySetSegment("Orders").appendKeySegment(8).appendNavigationSegment("CustomerForOrder"). appendRefSegment(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffset.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffset.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffset.java index 44dc72d..3f1d27a 100644 --- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffset.java +++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffset.java @@ -292,25 +292,22 @@ public final class EdmDateTimeOffset extends SingletonPrimitiveType { final Integer precision) throws IllegalArgumentException { if (fractionalSeconds > 0) { - if (precision == null || precision < String.valueOf(fractionalSeconds).length()) { - throw new IllegalArgumentException(); - } - - // Keep output similar to Calendar's, if possible - String fractionals = NANO_FORMAT.get().format(fractionalSeconds); - boolean canStart = false; - int firstZeroAfterNonZeroIdx = -1; - for (int i = 0; i < fractionals.length() && firstZeroAfterNonZeroIdx == -1; i++) { - if ('0' != fractionals.charAt(i)) { - canStart = true; - } else if (canStart && '0' == fractionals.charAt(i)) { - firstZeroAfterNonZeroIdx = i; + String formatted = NANO_FORMAT.get().format(fractionalSeconds); + int actualLength = formatted.length(); + boolean nonZeroFound = false; + for (int i = formatted.length() - 1; i >= 0 && !nonZeroFound; i--) { + if ('0' == formatted.charAt(i)) { + actualLength--; + } else { + nonZeroFound = true; } } - if (firstZeroAfterNonZeroIdx != -1 && 0 == Integer.valueOf(fractionals.substring(firstZeroAfterNonZeroIdx))) { - fractionals = fractionals.substring(0, firstZeroAfterNonZeroIdx); + + if (precision == null || precision < actualLength) { + throw new IllegalArgumentException(); } - result.append('.').append(fractionals); + + result.append('.').append(formatted.substring(0, actualLength)); } } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/e4a4f9e6/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffsetTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffsetTest.java b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffsetTest.java index 4e49a62..e03eff7 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffsetTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDateTimeOffsetTest.java @@ -62,6 +62,15 @@ public class EdmDateTimeOffsetTest extends PrimitiveTypeBaseTest { dateTime.setTimeZone(TimeZone.getTimeZone("GMT+11:00")); assertEquals("2012-02-29T01:02:03+11:00", instance.valueToString(dateTime, null, null, null, null, null)); + dateTime.set(Calendar.MILLISECOND, 503); + assertEquals("2012-02-29T01:02:03.503+11:00", instance.valueToString(dateTime, null, null, 3, null, null)); + + dateTime.set(Calendar.MILLISECOND, 530); + assertEquals("2012-02-29T01:02:03.53+11:00", instance.valueToString(dateTime, null, null, 3, null, null)); + + dateTime.set(Calendar.MILLISECOND, 53); + assertEquals("2012-02-29T01:02:03.053+11:00", instance.valueToString(dateTime, null, null, 3, null, null)); + final Long millis = 1330558323007L; assertEquals("2012-02-29T23:32:03.007Z", instance.valueToString(millis, null, null, 3, null, null)); assertEquals("1969-12-31T23:59:59.9Z", instance.valueToString(-100L, null, null, 1, null, null));
