[OLINGO-446] support for $ref serialization
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/a2a07242 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/a2a07242 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/a2a07242 Branch: refs/heads/OLINGO-632_OSGi-Support Commit: a2a07242ec7fb726367e72a73e874ae5348b9a25 Parents: bec8cb3 Author: Christian Holzer <[email protected]> Authored: Fri May 8 17:46:40 2015 +0200 Committer: Christian Holzer <[email protected]> Committed: Tue May 19 14:33:19 2015 +0200 ---------------------------------------------------------------------- .../tecsvc/client/EntityReferencesITCase.java | 215 +++++++++++++++ .../fit/tecsvc/http/EntityReferenceITCase.java | 91 +++++++ .../server/api/serializer/ODataSerializer.java | 28 ++ .../serializer/json/ODataJsonSerializer.java | 90 ++++++- .../serializer/xml/ODataXmlSerializerImpl.java | 16 ++ .../processor/TechnicalEntityProcessor.java | 261 +++++++++++++------ .../json/ODataJsonSerializerTest.java | 47 ++++ .../core/uri/antlr/TestFullResourcePath.java | 5 +- 8 files changed, 666 insertions(+), 87 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java new file mode 100644 index 0000000..e07b804 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/EntityReferencesITCase.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.fit.tecsvc.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.net.URI; +import java.util.List; + +import org.apache.olingo.client.api.EdmEnabledODataClient; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.communication.ODataClientErrorException; +import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; +import org.apache.olingo.client.api.domain.ClientEntity; +import org.apache.olingo.client.api.domain.ClientEntitySet; +import org.apache.olingo.client.core.ODataClientFactory; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.junit.Test; + +public class EntityReferencesITCase extends AbstractBaseTestITCase { + + private static final String SERVICE_URI = TecSvcConst.BASE_URI; + private static final String ES_ALL_PRIM = "ESAllPrim"; + private static final String PROPERTY_INT_16 = "PropertyInt16"; + private static final String ES_SERVER_SIDE_PAGING = "ESServerSidePaging"; + private static final String NAV_PROPERTY_ET_TWO_PRIM_ONE = "NavPropertyETTwoPrimOne"; + private static final String NAV_PROPERTY_ET_TWO_PRIM_MANY = "NavPropertyETTwoPrimMany"; + private static final String DESCENDING = " desc"; + + @Test + public void testOrderBy() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .appendRefSegment() + .orderBy(PROPERTY_INT_16).build(); + + executeCollection(uri, 3, "ESAllPrim(-32768)", "ESAllPrim(0)", "ESAllPrim(32767)"); + } + + @Test + public void testOrderByReverse() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .appendRefSegment() + .orderBy(PROPERTY_INT_16 + DESCENDING).build(); + + executeCollection(uri, 3, "ESAllPrim(32767)", "ESAllPrim(0)", "ESAllPrim(-32768)"); + } + + @Test + public void testNavigationToOne() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM).appendKeySegment(32767) + .appendNavigationSegment(NAV_PROPERTY_ET_TWO_PRIM_ONE).appendRefSegment().build(); + + final ODataRetrieveResponse<ClientEntity> response = client.getRetrieveRequestFactory() + .getEntityRequest(uri) + .execute(); + + assertEquals("ESTwoPrim(32767)", response.getBody().getId().toASCIIString()); + } + + @Test + public void testNavigationToMany() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM).appendKeySegment(0) + .appendNavigationSegment(NAV_PROPERTY_ET_TWO_PRIM_MANY).orderBy(PROPERTY_INT_16) + .appendRefSegment().build(); + + executeCollection(uri, 3, "ESTwoPrim(-32766)", "ESTwoPrim(32766)", "ESTwoPrim(32767)"); + } + + @Test + public void testFilter() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM).appendRefSegment() + .filter("PropertyDecimal eq 34").build(); + + executeCollection(uri, 1, "ESAllPrim(32767)"); + } + + @Test + public void testCount() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM).appendRefSegment().count(true).build(); + + final ODataRetrieveResponse<ClientEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + assertEquals(Integer.valueOf(3), response.getBody().getCount()); + } + + @Test + public void testSkip() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .appendRefSegment() + .orderBy(PROPERTY_INT_16).skip(2).build(); + + executeCollection(uri, 1, "ESAllPrim(32767)"); + } + + @Test + public void testServerDrivenPaging() { + final ODataClient client = getClient(); + URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_SERVER_SIDE_PAGING) + .appendRefSegment().build(); + + int entityId = 1; + final int EXPECTED_REQUESTS = 51; + for(int requestCount = 0; requestCount < EXPECTED_REQUESTS; requestCount++) { + ODataRetrieveResponse<ClientEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + List<ClientEntity> entities = response.getBody().getEntities(); + assertEquals(Math.min(10, 503 - (entityId - 1)), entities.size()); + + for( int i = 0; i < entities.size(); i++) { + final String entityID = entities.get(i).getId().toASCIIString(); + assertEquals(ES_SERVER_SIDE_PAGING + "(" + (entityId++) + ")", entityID); + } + + if(requestCount < (EXPECTED_REQUESTS - 1)) { + assertNotNull(response.getBody().getNext()); + } else { + assertNull(response.getBody().getNext()); + } + uri = response.getBody().getNext(); + } + } + + @Test + public void testResponseNonExistingEntity() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM).appendKeySegment(0) + .appendNavigationSegment(NAV_PROPERTY_ET_TWO_PRIM_ONE).appendRefSegment().build(); + + try { + client.getRetrieveRequestFactory() + .getEntityRequest(uri) + .execute(); + fail(); + } catch(ODataClientErrorException e) { + assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), e.getStatusLine().getStatusCode()); + } + } + + @Test + public void testEmptyCollection() { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(ES_ALL_PRIM) + .appendKeySegment(-32768) + .appendNavigationSegment(NAV_PROPERTY_ET_TWO_PRIM_MANY) + .appendRefSegment().build(); + + executeCollection(uri, 0, new String[0]); + } + + private void executeCollection(final URI uri, final int count, final String... expected) { + final ODataClient client = getClient(); + final ODataRetrieveResponse<ClientEntitySet> response = client.getRetrieveRequestFactory() + .getEntitySetRequest(uri) + .execute(); + + final List<ClientEntity> entities = response.getBody().getEntities(); + assertEquals(count, entities.size()); + + for( int i = 0; i < entities.size(); i++) { + final String entityID = entities.get(i).getId().toASCIIString(); + assertEquals(expected[i], entityID); + } + } + + @Override + protected ODataClient getClient() { + final EdmEnabledODataClient client = ODataClientFactory.getEdmEnabledClient(SERVICE_URI); + client.getConfiguration().setDefaultPubFormat(ODataFormat.JSON); + + return client; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/EntityReferenceITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/EntityReferenceITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/EntityReferenceITCase.java new file mode 100644 index 0000000..bcfc096 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/EntityReferenceITCase.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.fit.tecsvc.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.apache.olingo.server.core.deserializer.batch.BufferedReaderIncludingLineEndings; +import org.junit.Test; + +public class EntityReferenceITCase extends AbstractBaseTestITCase { + + private static final String CONTEXT_ENTITY_REFERENCE = "\"@odata.context\":\"$metadata#$ref\""; + private static final String CONTEXT_COLLECTION_REFERENCE = "\"@odata.context\":\"$metadata#Collection($ref)"; + private static final String SERVICE_URI = TecSvcConst.BASE_URI; + + @Test + public void testContextURlSingleEntity() throws Exception { + URL url = new URL(SERVICE_URI + "/ESAllPrim(0)/$ref"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json"); + connection.setRequestMethod("GET"); + connection.connect(); + + int code = connection.getResponseCode(); + assertEquals(200, code); + + final String content = getString(connection.getInputStream()); + assertTrue(content.contains(CONTEXT_ENTITY_REFERENCE)); + } + + @Test + public void testContextURLEntityCollection() throws Exception { + URL url = new URL(SERVICE_URI + "/ESAllPrim/$ref"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json"); + connection.setRequestMethod("GET"); + connection.connect(); + + int code = connection.getResponseCode(); + assertEquals(200, code); + + final String content = getString(connection.getInputStream()); + assertTrue(content.contains(CONTEXT_COLLECTION_REFERENCE)); + } + + private String getString(final InputStream in) throws Exception { + final StringBuilder builder = new StringBuilder(); + final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in)); + + String line; + while((line = reader.readLine()) != null) { + builder.append(line); + } + + reader.close(); + return builder.toString(); + } + + @Override + protected ODataClient getClient() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java index dfec888..470d090 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java @@ -18,11 +18,13 @@ */ package org.apache.olingo.server.api.serializer; +import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.server.api.ODataServerError; @@ -111,4 +113,30 @@ public interface ODataSerializer { */ SerializerResult complexCollection(ServiceMetadata metadata, EdmComplexType type, Property property, ComplexSerializerOptions options) throws SerializerException; + + /** + * Writes a single entity reference into an InputStream + * + * @param metadata Metadata for the service + * @param edmEntitySet {@link EdmEntitySet} + * @param entity data of the entity + * @param contextUrl {@link ContextURL} + * @return Serialized entity reference + * @throws SerializerException + */ + SerializerResult reference(ServiceMetadata metadata, EdmEntitySet edmEntitySet, Entity entity, + final ContextURL contextUrl) throws SerializerException; + + /** + * Writes entity collection references into an InputStream + * + * @param metadata Metadata for the service + * @param edmEntitySet {@link EdmEntitySet} + * @param entityCollection data of the entity collection + * @param contextURL + * @return Serialized entity reference + * @throws SerializerException + */ + SerializerResult referenceCollection(ServiceMetadata metadata, EdmEntitySet edmEntitySet, + EntityCollection entityCollection, final ContextURL contextURL) throws SerializerException; } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java index 7650894..65ad204 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java @@ -33,6 +33,7 @@ import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; @@ -52,6 +53,7 @@ import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.serializer.SerializerResult; +import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; @@ -59,6 +61,7 @@ import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder; import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper; +import org.apache.olingo.server.core.uri.UriHelperImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -145,7 +148,7 @@ public class ODataJsonSerializer implements ODataSerializer { if (options != null && options.getCount() != null && options.getCount().getValue() && entitySet.getCount() != null) { - json.writeNumberField(Constants.JSON_COUNT, entitySet.getCount()); + writeCount(entitySet, json); } json.writeFieldName(Constants.VALUE); if (options == null) { @@ -155,7 +158,7 @@ public class ODataJsonSerializer implements ODataSerializer { options.getExpand(), options.getSelect(), options.onlyReferences(), json); } if (entitySet.getNext() != null) { - json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); + writeNextLink(entitySet, json); } json.close(); } catch (final IOException e) { @@ -639,4 +642,87 @@ public class ODataJsonSerializer implements ODataSerializer { } return SerializerResultImpl.with().content(buffer.getInputStream()).build(); } + + @Override + public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, + final Entity entity, final ContextURL contextURL) throws SerializerException { + + final CircleStreamBuffer buffer = new CircleStreamBuffer(); + final UriHelper uriHelper = new UriHelperImpl(); + + try { + final JsonGenerator json = new JsonFactory().createGenerator(buffer.getOutputStream()); + writeReference(edmEntitySet, entity, contextURL, uriHelper, json); + + json.close(); + } catch (IOException e) { + throw new SerializerException("An I/O exception occurred.", e, SerializerException.MessageKeys.IO_EXCEPTION); + } + + return SerializerResultImpl.with().content(buffer.getInputStream()).build(); + } + + @Override + public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, + final EntityCollection entityCollection, final ContextURL contextURL) throws SerializerException { + + final CircleStreamBuffer buffer = new CircleStreamBuffer(); + final UriHelper uriHelper = new UriHelperImpl(); + + try { + final JsonGenerator json = new JsonFactory().createGenerator(buffer.getOutputStream()); + json.writeStartObject(); + + if(entityCollection.getCount() != null) { + writeCount(entityCollection, json); + } + + json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString()); + writeReferenceCollection(metadata, edmEntitySet, entityCollection, uriHelper,json); + + if(entityCollection.getNext() != null) { + writeNextLink(entityCollection, json); + } + + json.writeEndObject(); + json.close(); + } catch (IOException e) { + throw new SerializerException("An I/O exception occurred.", e, SerializerException.MessageKeys.IO_EXCEPTION); + } + + return SerializerResultImpl.with().content(buffer.getInputStream()).build(); + } + + protected void writeReferenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, + final EntityCollection entityCollection, final UriHelper uriHelper, final JsonGenerator json) + throws IOException, SerializerException { + + json.writeArrayFieldStart(Constants.VALUE); + + for(final Entity entity : entityCollection.getEntities()) { + writeReference(edmEntitySet, entity, null, uriHelper, json); + } + + json.writeEndArray(); + } + + protected void writeReference(final EdmEntitySet edmEntitySet, final Entity entity, final ContextURL contextURL, + final UriHelper uriHelper, final JsonGenerator json) throws IOException, SerializerException { + + json.writeStartObject(); + if(contextURL != null) { + json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString()); + } + + json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity)); + json.writeEndObject(); + } + + private void writeCount(final EntityCollection entitySet, JsonGenerator json) throws IOException { + json.writeNumberField(Constants.JSON_COUNT, entitySet.getCount()); + } + + private void writeNextLink(final EntityCollection entitySet, JsonGenerator json) throws IOException { + json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java index b15450b..795a330 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java @@ -22,11 +22,13 @@ import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; +import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.server.api.ODataServerError; @@ -134,4 +136,18 @@ public class ODataXmlSerializerImpl implements ODataSerializer { throw new SerializerException("Serialization not implemented for XML format.", SerializerException.MessageKeys.NOT_IMPLEMENTED); } + + @Override + public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntotySet, + final Entity entity, final ContextURL contextURL) throws SerializerException { + throw new SerializerException("Serialization not implemented for XML format.", + SerializerException.MessageKeys.NOT_IMPLEMENTED); + } + + @Override + public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, + final EntityCollection entityCollection, final ContextURL contextURL) throws SerializerException { + throw new SerializerException("Serialization not implemented for XML format.", + SerializerException.MessageKeys.NOT_IMPLEMENTED); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java index 535280b..167a15c 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java @@ -48,6 +48,8 @@ import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor; import org.apache.olingo.server.api.processor.EntityCollectionProcessor; import org.apache.olingo.server.api.processor.EntityProcessor; import org.apache.olingo.server.api.processor.MediaEntityProcessor; +import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; +import org.apache.olingo.server.api.processor.ReferenceProcessor; import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; import org.apache.olingo.server.api.serializer.EntitySerializerOptions; import org.apache.olingo.server.api.serializer.ODataSerializer; @@ -57,6 +59,7 @@ import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriResourceAction; import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceFunction; +import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.tecsvc.data.DataProvider; @@ -76,7 +79,7 @@ import org.apache.olingo.server.tecsvc.processor.queryoptions.options.TopHandler public class TechnicalEntityProcessor extends TechnicalProcessor implements EntityCollectionProcessor, ActionEntityCollectionProcessor, CountEntityCollectionProcessor, EntityProcessor, ActionEntityProcessor, MediaEntityProcessor, - ActionVoidProcessor { + ActionVoidProcessor, ReferenceCollectionProcessor, ReferenceProcessor { public TechnicalEntityProcessor(final DataProvider dataProvider, final ServiceMetadata serviceMetadata) { super(dataProvider, serviceMetadata); @@ -87,62 +90,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final ContentType requestedContentType) throws ODataApplicationException, SerializerException { validateOptions(uriInfo.asUriInfoResource()); - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); - final EdmEntityType edmEntityType = edmEntitySet == null ? - (EdmEntityType) ((UriResourceFunction) uriInfo.getUriResourceParts() - .get(uriInfo.getUriResourceParts().size() - 1)).getType() : - edmEntitySet.getEntityType(); - - final EntityCollection entitySetInitial = readEntityCollection(uriInfo); - if (entitySetInitial == null) { - throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); - } else { - // Modifying the original entitySet means modifying the "database", so we have to make a shallow - // copy of the entity set (new EntitySet, but exactly the same data) - EntityCollection entitySet = new EntityCollection(); - entitySet.getEntities().addAll(entitySetInitial.getEntities()); - - // Apply system query options - FilterHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), entitySet, edmEntitySet); - CountHandler.applyCountSystemQueryOption(uriInfo.getCountOption(), entitySet); - OrderByHandler.applyOrderByOption(uriInfo.getOrderByOption(), entitySet, edmEntitySet); - SkipHandler.applySkipSystemQueryHandler(uriInfo.getSkipOption(), entitySet); - TopHandler.applyTopSystemQueryOption(uriInfo.getTopOption(), entitySet); - - ServerSidePagingHandler.applyServerSidePaging(uriInfo.getSkipTokenOption(), - entitySet, - edmEntitySet, - request.getRawRequestUri()); - - // Apply expand system query option - final ODataFormat format = ODataFormat.fromContentType(requestedContentType); - ODataSerializer serializer = odata.createSerializer(format); - final ExpandOption expand = uriInfo.getExpandOption(); - final SelectOption select = uriInfo.getSelectOption(); - - // Transform the entity graph to a tree. The construction is controlled by the expand tree. - // Apply all expand system query options to the tree.So the expanded navigation properties can be modified - // for serialization,without affecting the data stored in the database. - final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); - final EntityCollection entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet, - edmEntitySet, - expand); - expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand); - - // Serialize - response.setContent(serializer.entityCollection( - serviceMetadata, - edmEntityType, - entitySetSerialization, - EntityCollectionSerializerOptions.with() - .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : - getContextUrl(edmEntitySet, edmEntityType, false, expand, select)) - .count(uriInfo.getCountOption()) - .expand(expand).select(select) - .build()).getContent()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); - } + processEntityCollection(request, response, uriInfo, requestedContentType, false); } @Override @@ -178,7 +126,8 @@ public class TechnicalEntityProcessor extends TechnicalProcessor } @Override - public void countEntityCollection(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo) + public void countEntityCollection(final ODataRequest request, final ODataResponse response, final UriInfo + uriInfo) throws ODataApplicationException, SerializerException { validateOptions(uriInfo.asUriInfoResource()); final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo); // including checks @@ -200,29 +149,8 @@ public class TechnicalEntityProcessor extends TechnicalProcessor public void readEntity(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, final ContentType requestedContentType) throws ODataApplicationException, SerializerException { validateOptions(uriInfo.asUriInfoResource()); - final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo); - final EdmEntityType edmEntityType = edmEntitySet == null ? - (EdmEntityType) ((UriResourceFunction) uriInfo.getUriResourceParts() - .get(uriInfo.getUriResourceParts().size() - 1)).getType() : - edmEntitySet.getEntityType(); - - final Entity entity = readEntity(uriInfo); - - final ODataFormat format = ODataFormat.fromContentType(requestedContentType); - final ExpandOption expand = uriInfo.getExpandOption(); - final SelectOption select = uriInfo.getSelectOption(); - final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); - final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand); - expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand); - - response.setContent(serializeEntity(entitySerialization, edmEntitySet, edmEntityType, format, expand, select) - .getContent()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); - if (entity.getETag() != null) { - response.setHeader(HttpHeader.ETAG, entity.getETag()); - } + processEntity(request, response, uriInfo, requestedContentType, false); } @Override @@ -274,7 +202,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor request.getRawBaseUri()).validate(edmEntitySet, deserializerResult.getEntity()); entity = dataProvider.create(edmEntitySet); - dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, true); + dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, + + true); expand = deserializerResult.getExpandTree(); } @@ -390,7 +320,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final ODataFormat format = ODataFormat.fromContentType(responseFormat); response.setContent(serializeEntity(entityResult.getEntity(), edmEntitySet, type, format, null, null) .getContent()); - response.setStatusCode((entityResult.isCreated() ? HttpStatusCode.CREATED : HttpStatusCode.OK).getStatusCode()); + response.setStatusCode((entityResult.isCreated() ? HttpStatusCode.CREATED : HttpStatusCode.OK).getStatusCode + + ()); response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); if (entityResult.getEntity().getETag() != null) { response.setHeader(HttpHeader.ETAG, entityResult.getEntity().getETag()); @@ -420,7 +352,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor } private ContextURL getContextUrl(final EdmEntitySet entitySet, final EdmEntityType entityType, - final boolean isSingleEntity, final ExpandOption expand, final SelectOption select) throws SerializerException { + final boolean isSingleEntity, final ExpandOption expand, final SelectOption select) throws + + SerializerException { Builder builder = ContextURL.with(); builder = entitySet == null ? isSingleEntity ? builder.type(entityType) : builder.asCollection().type(entityType) : @@ -431,9 +365,170 @@ public class TechnicalEntityProcessor extends TechnicalProcessor return builder.build(); } + @Override + public void readReference(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, + final ContentType requestedContentType) throws ODataApplicationException, SerializerException { + + processEntity(request, response, uriInfo, requestedContentType, true); + } + + @Override + public void createReference(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, + final ContentType requestFormat) throws ODataApplicationException, DeserializerException { + + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + @Override + public void updateReference(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, + final ContentType requestFormat) throws ODataApplicationException, DeserializerException { + + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + @Override + public void deleteReference(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo) + throws ODataApplicationException { + + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + @Override + public void readReferenceCollection(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, + final ContentType requestedContentType) throws ODataApplicationException, SerializerException { + + processEntityCollection(request, response, uriInfo, requestedContentType, true); + } + + private void processEntity(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, + final ContentType requestedContentType, final boolean isReference) + throws ODataApplicationException, SerializerException { + final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo); + final EdmEntityType edmEntityType = edmEntitySet == null ? + (EdmEntityType) ((UriResourceFunction) uriInfo.getUriResourceParts() + .get(uriInfo.getUriResourceParts().size() - 1)).getType() : + edmEntitySet.getEntityType(); + + final Entity entity = readEntity(uriInfo); + + final ODataFormat format = ODataFormat.fromContentType(requestedContentType); + final ExpandOption expand = uriInfo.getExpandOption(); + final SelectOption select = uriInfo.getSelectOption(); + + final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); + final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand); + expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand); + + final SerializerResult serializerResult = (isReference) ? + serializeReference(entity, edmEntitySet, format) + : serializeEntity(entitySerialization, edmEntitySet, edmEntityType, format, expand, select); + + + if (entity.getETag() != null) { + response.setHeader(HttpHeader.ETAG, entity.getETag()); + } + response.setContent(serializerResult.getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } + + private void processEntityCollection(final ODataRequest request, final ODataResponse response, + final UriInfo uriInfo, final ContentType requestedContentType, final boolean isReference) + throws ODataApplicationException, SerializerException { + + final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource()); + final EdmEntityType edmEntityType = edmEntitySet == null ? + (EdmEntityType) ((UriResourceFunction) uriInfo.getUriResourceParts() + .get(uriInfo.getUriResourceParts().size() - 1)).getType() : + edmEntitySet.getEntityType(); + + EntityCollection entitySetInitial = readEntityCollection(uriInfo); + if(entitySetInitial == null) { + entitySetInitial = new EntityCollection(); + } + + // Modifying the original entitySet means modifying the "database", so we have to make a shallow + // copy of the entity set (new EntitySet, but exactly the same data) + EntityCollection entitySet = new EntityCollection(); + entitySet.getEntities().addAll(entitySetInitial.getEntities()); + + // Apply system query options + FilterHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), entitySet, edmEntitySet); + CountHandler.applyCountSystemQueryOption(uriInfo.getCountOption(), entitySet); + OrderByHandler.applyOrderByOption(uriInfo.getOrderByOption(), entitySet, edmEntitySet); + SkipHandler.applySkipSystemQueryHandler(uriInfo.getSkipOption(), entitySet); + TopHandler.applyTopSystemQueryOption(uriInfo.getTopOption(), entitySet); + + ServerSidePagingHandler.applyServerSidePaging(uriInfo.getSkipTokenOption(), + entitySet, + edmEntitySet, + request.getRawRequestUri()); + + // Apply expand system query option + final ODataFormat format = ODataFormat.fromContentType(requestedContentType); + final ExpandOption expand = uriInfo.getExpandOption(); + final SelectOption select = uriInfo.getSelectOption(); + + // Transform the entity graph to a tree. The construction is controlled by the expand tree. + // Apply all expand system query options to the tree.So the expanded navigation properties can be modified + // for serialization,without affecting the data stored in the database. + final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); + final EntityCollection entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet, + edmEntitySet, + expand); + expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand); + final CountOption countOption = uriInfo.getCountOption(); + + // Serialize + final SerializerResult serializerResult = (isReference) ? + serializeReferenceCollection(entitySetSerialization, edmEntitySet, format) : + serializeEntiyCollection(entitySetSerialization, edmEntitySet, edmEntityType, format, + expand, select, countOption); + + response.setContent(serializerResult.getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); + } + + private SerializerResult serializeEntiyCollection(final EntityCollection entityCollection, + final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType, final ODataFormat format, + final ExpandOption expand, final SelectOption select, final CountOption countOption) + throws SerializerException { + + return odata.createSerializer(format).entityCollection( + serviceMetadata, + edmEntityType, + entityCollection, + EntityCollectionSerializerOptions.with() + .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : + getContextUrl(edmEntitySet, edmEntityType, false, expand, select)) + .count(countOption) + .expand(expand).select(select) + .build()); + } + + private SerializerResult serializeReferenceCollection(final EntityCollection entityCollection, + final EdmEntitySet edmEntitySet, final ODataFormat format) throws SerializerException { + + final ContextURL contextUrl = ContextURL.with().asCollection().suffix(Suffix.REFERENCE).build(); + final ODataSerializer serializer = odata.createSerializer(format); + + return serializer.referenceCollection(serviceMetadata, edmEntitySet, entityCollection, contextUrl); + } + + private SerializerResult serializeReference(final Entity entity, final EdmEntitySet edmEntitySet, + final ODataFormat format ) throws SerializerException { + + final ContextURL contextUrl = ContextURL.with().suffix(Suffix.REFERENCE).build(); + final ODataSerializer serializer = odata.createSerializer(format); + + return serializer.reference(serviceMetadata, edmEntitySet, entity, contextUrl); + } + private SerializerResult serializeEntity(final Entity entity, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType, final ODataFormat format, final ExpandOption expand, final SelectOption select) throws SerializerException { + return odata.createSerializer(format).entity( serviceMetadata, edmEntityType, http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index 2e42963..c867022 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -48,6 +48,7 @@ import org.apache.olingo.server.api.serializer.EntitySerializerOptions; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; @@ -681,4 +682,50 @@ public class ODataJsonSerializerTest { + "{\"PropertyInt16\":789,\"PropertyString\":\"TEST 3\"}]}", resultString); } + + @Test + public void entityReference() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(0); + + final SerializerResult serializerResult = serializer.reference(metadata, edmEntitySet, entity, + ContextURL.with().suffix(Suffix.REFERENCE).build()); + final String resultString = IOUtils.toString(serializerResult.getContent()); + + Assert.assertEquals("{\"@odata.context\":\"$metadata#$ref\",\"@odata.id\":\"ESAllPrim(32767)\"}", resultString); + } + + @Test + public void entityCollectionReference() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final EntityCollection entityCollection = data.readAll(edmEntitySet); + + final SerializerResult serializerResult = serializer.referenceCollection(metadata, + edmEntitySet, + entityCollection, + ContextURL.with().asCollection() + .suffix(Suffix.REFERENCE).build()); + + final String resultString = IOUtils.toString(serializerResult.getContent()); + + Assert.assertEquals("{\"@odata.context\":\"$metadata#Collection($ref)\",\"value\":[{\"@odata.id\":" + + "\"ESAllPrim(32767)\"},{\"@odata.id\":\"ESAllPrim(-32768)\"},{\"@odata.id\":" + + "\"ESAllPrim(0)\"}]}", resultString); + } + + @Test + public void entityCollectionReferenceEmpty() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); + final EntityCollection entityCollection = new EntityCollection(); + + final SerializerResult serializerResult = serializer.referenceCollection(metadata, + edmEntitySet, + entityCollection, + ContextURL.with().asCollection() + .suffix(Suffix.REFERENCE).build()); + + final String resultString = IOUtils.toString(serializerResult.getContent()); + + Assert.assertEquals("{\"@odata.context\":\"$metadata#Collection($ref)\",\"value\":[]}", resultString); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a2a07242/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java index 989b058..f5e625a 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestFullResourcePath.java @@ -4756,8 +4756,9 @@ public class TestFullResourcePath { .root().right().goPath() .first().isComplex("PropertyCompMixedEnumDef") .n().isComplex("PropertyEnumString").isType(EnumTypeProvider.nameENString).goUpFilterValidator(); - } + } + @Test public void filterOnCountAndRef() throws Exception { testUri.run("ESKeyNav/$count", "$filter=PropertyInt16 ge 0") @@ -4771,7 +4772,7 @@ public class TestFullResourcePath { .left().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false) .goUpFilterValidator().root().right().isLiteral("0"); } - + @Test public void testOrderby() throws UriParserException, UnsupportedEncodingException {
