You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ra...@apache.org on 2015/03/31 18:35:52 UTC
[06/11] olingo-odata4 git commit: OLINGO-573: New processing
framework on server side with single interface with TripPin as example
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
new file mode 100644
index 0000000..19a0387
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
@@ -0,0 +1,546 @@
+/*
+ * 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.server.example;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmAction;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmFunction;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.commons.core.data.EntitySetImpl;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.requests.ActionRequest;
+import org.apache.olingo.server.core.requests.DataRequest;
+import org.apache.olingo.server.core.requests.FunctionRequest;
+import org.apache.olingo.server.core.requests.MediaRequest;
+import org.apache.olingo.server.core.requests.MetadataRequest;
+import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
+import org.apache.olingo.server.core.responses.CountResponse;
+import org.apache.olingo.server.core.responses.EntityResponse;
+import org.apache.olingo.server.core.responses.EntitySetResponse;
+import org.apache.olingo.server.core.responses.MetadataResponse;
+import org.apache.olingo.server.core.responses.NoContentResponse;
+import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+import org.apache.olingo.server.core.responses.ServiceDocumentResponse;
+import org.apache.olingo.server.core.responses.ServiceResponse;
+import org.apache.olingo.server.core.responses.ServiceResponseVisior;
+import org.apache.olingo.server.core.responses.StreamResponse;
+
+public class TripPinHandler implements ServiceHandler {
+ private OData odata;
+ private ServiceMetadata serviceMetadata;
+ private final TripPinDataModel dataModel;
+
+ public TripPinHandler(TripPinDataModel datamodel) {
+ this.dataModel = datamodel;
+ }
+
+ @Override
+ public void init(OData odata, ServiceMetadata serviceMetadata) {
+ this.odata = odata;
+ this.serviceMetadata = serviceMetadata;
+ }
+
+ @Override
+ public void readMetadata(MetadataRequest request, MetadataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.writeMetadata();
+ }
+
+ @Override
+ public void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.writeServiceDocument(request.getODataRequest().getRawBaseUri());
+ }
+
+ static class EntityDetails {
+ EntitySet entitySet = null;
+ Entity entity = null;
+ EdmEntityType entityType;
+ String navigationProperty;
+ Entity parentEntity = null;
+ }
+
+ private EntityDetails process(final DataRequest request) throws ODataApplicationException {
+ EntitySet entitySet = null;
+ Entity entity = null;
+ EdmEntityType entityType;
+ Entity parentEntity = null;
+
+ if (request.isSingleton()) {
+ EdmSingleton singleton = request.getUriResourceSingleton().getSingleton();
+ entityType = singleton.getEntityType();
+ if (singleton.getName().equals("Me")) {
+ entitySet = this.dataModel.getEntitySet("People");
+ entity = entitySet.getEntities().get(0);
+ }
+ } else {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ entityType = edmEntitySet.getEntityType();
+ List<UriParameter> keys = request.getKeyPredicates();
+
+ // TODO: This example so far ignores all system options; but a real
+ // service should not
+ if (keys != null && !keys.isEmpty()) {
+ entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+ } else {
+ int skip = 0;
+ if (request.getUriInfo().getSkipTokenOption() != null) {
+ skip = Integer.parseInt(request.getUriInfo().getSkipTokenOption().getValue());
+ }
+ int pageSize = getPageSize(request);
+ entitySet = this.dataModel.getEntitySet(edmEntitySet.getName(), skip, pageSize);
+ if (entitySet.getEntities().size() == pageSize) {
+ try {
+ entitySet.setNext(new URI(request.getODataRequest().getRawRequestUri() + "?$skiptoken="
+ + (skip + pageSize)));
+ } catch (URISyntaxException e) {
+ throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
+ }
+ }
+ }
+ }
+ EntityDetails details = new EntityDetails();
+
+ if (!request.getNavigations().isEmpty() && entity != null) {
+ UriResourceNavigation lastNavigation = request.getNavigations().getLast();
+ for (UriResourceNavigation nav : request.getNavigations()) {
+ entityType = nav.getProperty().getType();
+ if (nav.isCollection()) {
+ entitySet = this.dataModel.getNavigableEntitySet(entity, nav);
+ } else {
+ parentEntity = entity;
+ entity = this.dataModel.getNavigableEntity(parentEntity, nav);
+ }
+ }
+ details.navigationProperty = lastNavigation.getProperty().getName();
+ }
+
+ details.entity = entity;
+ details.entitySet = entitySet;
+ details.entityType = entityType;
+ details.parentEntity = parentEntity;
+ return details;
+ }
+
+ @Override
+ public <T extends ServiceResponse> void read(final DataRequest request, final T response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ final EntityDetails details = process(request);
+
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(CountResponse response) throws ODataTranslatedException, ODataApplicationException {
+ response.writeCount(details.entitySet.getCount());
+ }
+
+ @Override
+ public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+ Property property = details.entity.getProperty(edmProperty.getName());
+ response.write(property.getValue());
+ }
+
+ @Override
+ public void visit(PropertyResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+ Property property = details.entity.getProperty(edmProperty.getName());
+ response.writeProperty(edmProperty.getType(), property);
+ }
+
+ @Override
+ public void visit(StreamResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ // stream property response
+ response.writeStreamResponse(new ByteArrayInputStream("dummy".getBytes()),
+ ContentType.APPLICATION_OCTET_STREAM);
+ }
+
+ @Override
+ public void visit(EntitySetResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ if (request.getPreference("odata.maxpagesize") != null) {
+ response.writeHeader("Preference-Applied", request.getPreference("odata.maxpagesize"));
+ }
+ if (details.entity == null && !request.getNavigations().isEmpty()) {
+ response.writeReadEntitySet(details.entityType, new EntitySetImpl());
+ } else {
+ response.writeReadEntitySet(details.entityType, details.entitySet);
+ }
+ }
+
+ @Override
+ public void visit(EntityResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ if (details.entity == null && !request.getNavigations().isEmpty()) {
+ response.writeNoContent(true);
+ } else {
+ response.writeReadEntity(details.entityType, details.entity);
+ }
+ }
+ });
+ }
+
+ private int getPageSize(DataRequest request) {
+ String size = request.getPreference("odata.maxpagesize");
+ if (size == null) {
+ return 8;
+ }
+ return Integer.parseInt(size);
+ }
+
+ @Override
+ public void createEntity(DataRequest request, Entity entity, EntityResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+
+ String location = buildLocation(entity, edmEntitySet.getName(), edmEntitySet.getEntityType());
+ Entity created = this.dataModel.createEntity(edmEntitySet.getName(), entity, location);
+
+ try {
+ // create references, they come in "@odata.bind" value
+ List<Link> bindings = entity.getNavigationBindings();
+ if (bindings != null & !bindings.isEmpty()) {
+ for (Link link : bindings) {
+ String navigationProperty = link.getTitle();
+ String uri = link.getBindingLink();
+ if (uri != null) {
+ DataRequest bindingRequest = request.parseLink(new URI(uri));
+
+ Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+
+ this.dataModel.addNavigationLink(navigationProperty, created, reference);
+
+ } else {
+ for (String binding : link.getBindingLinks()) {
+ DataRequest bindingRequest = request.parseLink(new URI(binding));
+
+ Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+
+ this.dataModel.addNavigationLink(navigationProperty, created, reference);
+ }
+ }
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
+ }
+
+ response.writeCreatedEntity(edmEntitySet.getEntityType(), created, location);
+ }
+
+ static String buildLocation(Entity entity, String name, EdmEntityType type) {
+ String location = "/" + name + "(";
+ int i = 0;
+ boolean usename = type.getKeyPredicateNames().size() > 1;
+
+ for (String key : type.getKeyPredicateNames()) {
+ if (i > 0) {
+ location += ",";
+ }
+ i++;
+ if (usename) {
+ location += (key + "=");
+ }
+ if (entity.getProperty(key).getType().equals("Edm.String")) {
+ location = location + "'" + entity.getProperty(key).getValue().toString() + "'";
+ } else {
+ location = location + entity.getProperty(key).getValue().toString();
+ }
+ }
+ location += ")";
+ return location;
+ }
+
+ @Override
+ public void updateEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
+ EntityResponse response) throws ODataTranslatedException, ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ @Override
+ public void deleteEntity(DataRequest request, String eTag, EntityResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
+ if (entity == null) {
+ response.writeNotFound(true);
+ return;
+ }
+ String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
+ boolean removed = this.dataModel.deleteEntity(edmEntitySet.getName(), eTag, key, entity
+ .getProperty(key).getValue());
+
+ if (removed) {
+ response.writeDeletedEntityOrReference();
+ } else {
+ response.writeNotFound(true);
+ }
+ }
+
+ @Override
+ public void updateProperty(DataRequest request, final Property property, boolean merge,
+ String entityETag, PropertyResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
+ if (entity == null) {
+ response.writeNotFound(true);
+ return;
+ }
+
+ String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
+ boolean replaced = this.dataModel.updateProperty(edmEntitySet.getName(), entityETag, key,
+ entity.getProperty(key).getValue(), property);
+
+ if (replaced) {
+ if (property.getValue() == null) {
+ response.writePropertyDeleted();
+ } else {
+ response.writePropertyUpdated();
+ }
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public <T extends ServiceResponse> void invoke(FunctionRequest request, HttpMethod method,
+ T response) throws ODataTranslatedException, ODataApplicationException {
+ EdmFunction function = request.getFunction();
+ if (function.getName().equals("GetNearestAirport")) {
+
+ final EdmEntityType type = serviceMetadata.getEdm().getEntityContainer(null)
+ .getEntitySet("Airports").getEntityType();
+
+ EntitySet es = this.dataModel.getEntitySet("Airports");
+ int i = new Random().nextInt(es.getEntities().size());
+ final Entity entity = es.getEntities().get(i);
+
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(EntityResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeReadEntity(type, entity);
+ }
+ });
+ }
+ }
+
+ @Override
+ public <T extends ServiceResponse> void invoke(ActionRequest request, String eTag, T response)
+ throws ODataTranslatedException, ODataApplicationException {
+ EdmAction action = request.getAction();
+ if (action.getName().equals("ResetDataSource")) {
+ try {
+ this.dataModel.loadData();
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(NoContentResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeNoContent();
+ }
+ });
+ } catch (Exception e) {
+ response.writeServerError(true);
+ }
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void readMediaStream(MediaRequest request, StreamResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List<UriParameter> keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ InputStream contents = this.dataModel.readMedia(entity);
+ response.writeStreamResponse(contents, request.getResponseContentType());
+ }
+
+ @Override
+ public void upsertMediaStream(MediaRequest request, String entityETag, InputStream mediaContent,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List<UriParameter> keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ if (mediaContent == null) {
+ boolean deleted = this.dataModel.deleteMedia(entity);
+ if (deleted) {
+ response.writeNoContent();
+ } else {
+ response.writeNotFound();
+ }
+ } else {
+ boolean updated = this.dataModel.updateMedia(entity, mediaContent);
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+ }
+
+ @Override
+ public void upsertStreamProperty(DataRequest request, String entityETag, InputStream streamContent,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List<UriParameter> keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ EdmProperty property = request.getUriResourceProperty().getProperty();
+
+ if (streamContent == null) {
+ boolean deleted = this.dataModel.deleteStream(entity, property);
+ if (deleted) {
+ response.writeNoContent();
+ } else {
+ response.writeNotFound();
+ }
+ } else {
+ boolean updated = this.dataModel.updateStream(entity, property, streamContent);
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+ }
+
+ @Override
+ public void addReference(DataRequest request, String entityETag, List<URI> references,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+
+ final EntityDetails details = process(request);
+
+ for (URI reference : references) {
+ DataRequest bindingRequest = request.parseLink(reference);
+ Entity linkEntity = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+ this.dataModel.addNavigationLink(details.navigationProperty, details.entity, linkEntity);
+ }
+ response.writeNoContent();
+ }
+
+ @Override
+ public void updateReference(DataRequest request, String entityETag, URI updateId,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ // this single valued navigation.
+ final EntityDetails details = process(request);
+ DataRequest updateRequest = request.parseLink(updateId);
+ Entity updateEntity = this.dataModel.getEntity(updateRequest.getEntitySet().getName(),
+ updateRequest.getKeyPredicates());
+ boolean updated = false;
+ if (updateEntity != null) {
+ updated = this.dataModel.updateNavigationLink(details.navigationProperty,
+ details.parentEntity, updateEntity);
+ }
+
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void deleteReference(DataRequest request, URI deleteId, String entityETag,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ boolean removed = false;
+ if (deleteId != null) {
+ final EntityDetails details = process(request);
+ DataRequest deleteRequest = request.parseLink(deleteId);
+ Entity deleteEntity = this.dataModel.getEntity(deleteRequest.getEntitySet().getName(),
+ deleteRequest.getKeyPredicates());
+ if (deleteEntity != null) {
+ removed = this.dataModel.removeNavigationLink(details.navigationProperty, details.entity,
+ deleteEntity);
+ }
+ } else {
+ // this single valued navigation.
+ final EntityDetails details = process(request);
+ removed = this.dataModel.removeNavigationLink(details.navigationProperty,
+ details.parentEntity, details.entity);
+ }
+ if (removed) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void anyUnsupported(ODataRequest request, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.setStatusCode(500);
+ }
+
+ @Override
+ public String startTransaction() {
+ return null;
+ }
+
+ @Override
+ public void commit(String txnId) {
+ }
+
+ @Override
+ public void rollback(String txnId) {
+ }
+
+ @Override
+ public void crossJoin(DataRequest dataRequest, List<String> entitySetNames, ODataResponse response) {
+ response.setStatusCode(200);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
new file mode 100644
index 0000000..4b26b8e
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
@@ -0,0 +1,756 @@
+/*
+ * 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.server.example;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.apache.olingo.commons.core.Encoder;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ * Please note that NONE of the system query options are developed in the sample
+ * service like $filter, $orderby etc. So using those options will be ignored
+ * right now. These tests designed to test the framework, all options are responsibilities
+ * of service developer.
+ */
+public class TripPinServiceTest {
+ private static Server server = new Server();
+ private static String baseURL;
+ private static HttpClient http = new HttpClient();
+
+ @BeforeClass
+ public static void beforeTest() throws Exception {
+ ServerConnector connector = new ServerConnector(server);
+ server.setConnectors(new Connector[] { connector });
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/trippin");
+ context.addServlet(new ServletHolder(new TripPinServlet()), "/*");
+ server.setHandler(context);
+ server.start();
+ int port = connector.getLocalPort();
+ http.start();
+ baseURL = "http://localhost:"+port+"/trippin";
+ }
+
+ @AfterClass
+ public static void afterTest() throws Exception {
+ server.stop();
+ }
+
+ @Test
+ public void testEntitySet() throws Exception {
+ ContentResponse response = http.newRequest(baseURL + "/People")
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertEquals(baseURL+"/People?$skiptoken=8", node.get("@odata.nextLink").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("russellwhyte", person.get("UserName").asText());
+ }
+
+ private JsonNode getJSONNode(ContentResponse response) throws IOException,
+ JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode node = objectMapper.readTree(response.getContent());
+ return node;
+ }
+
+ @Test
+ public void testReadEntitySetWithPaging() throws Exception {
+ ContentResponse response = http.newRequest(baseURL + "/People")
+ .header("Prefer", "odata.maxpagesize=10").send();
+
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertEquals(baseURL+"/People?$skiptoken=10", node.get("@odata.nextLink").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("russellwhyte", person.get("UserName").asText());
+
+ assertNotNull(response.getHeaders().get("Preference-Applied"));
+ }
+
+ @Test
+ public void testReadEntityWithKey() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Airlines/$entity", node.get("@odata.context").asText());
+ assertEquals("American Airlines", node.get("Name").asText());
+ }
+
+ @Test
+ public void testReadEntityWithNonExistingKey() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('OO')");
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testRead$Count() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines/$count");
+ assertEquals(200, response.getStatus());
+ assertEquals("15", response.getContentAsString());
+ }
+
+ @Test
+ public void testReadPrimitiveProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Airlines('AA')/Name", node.get("@odata.context").asText());
+ assertEquals("American Airlines", node.get("value").asText());
+ }
+
+ @Test
+ public void testReadNonExistentProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Unknown");
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testReadPrimitiveArrayProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Emails");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("Russell@example.com", ((ArrayNode)node.get("value")).get(0).asText());
+ assertEquals("Russell@contoso.com", ((ArrayNode)node.get("value")).get(1).asText());
+ }
+
+ @Test
+ public void testReadPrimitivePropertyValue() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name/$value");
+ assertEquals(200, response.getStatus());
+ assertEquals("American Airlines", response.getContentAsString());
+ }
+
+ @Test @Ignore
+ // TODO: Support geometry types to make this run
+ public void testReadComplexProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airports('KSFO')/Location");
+ fail("support geometry type");
+ }
+
+ @Test
+ public void testReadComplexArrayProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/AddressInfo");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/AddressInfo", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("187 Suffolk Ln.", ((ArrayNode)node.get("value")).get(0).get("Address").asText());
+ }
+
+ @Test
+ public void testReadMedia() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Photos(1)/$value");
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMedia() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Photos(1)/$value";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteMedia() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Photos(1)/$value";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testCreateStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.POST)
+ .send();
+ // method not allowed
+ assertEquals(405, response.getStatus());
+ }
+
+ @Test
+ public void testCreateStream2() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testLambdaAny() throws Exception {
+ // this is just testing to see the labba expresions are going through the
+ // framework, none of the system options are not implemented in example service
+ String query = "Friends/any(d:d/UserName eq 'foo')";
+ ContentResponse response = http.newRequest(baseURL + "/People?$filter="+Encoder.encode(query))
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testSingleton() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Me");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Me", node.get("@odata.context").asText());
+ assertEquals("russellwhyte", node.get("UserName").asText());
+ }
+
+ @Test
+ public void testSelectOption() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')?$select=FirstName,LastName");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People(FirstName,LastName)/$entity", node.get("@odata.context").asText());
+ assertEquals("Russell", node.get("FirstName").asText());
+ }
+
+ @Test
+ public void testActionImportWithNoResponse() throws Exception {
+ ContentResponse response = http.POST(baseURL + "/ResetDataSource").send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testFunctionImport() throws Exception {
+ //TODO: fails because of lack of geometery support
+ ContentResponse response = http.GET(baseURL + "/GetNearestAirport(lat=23.0,lon=34.0)");
+ }
+
+ @Test
+ public void testBadReferences() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russelwhyte')/$ref");
+ assertEquals(405, response.getStatus());
+ }
+
+ @Test
+ public void testReadReferences() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Collection($ref)", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ }
+
+ @Test
+ public void testAddCollectionReferences() throws Exception {
+ //GET
+ ContentResponse response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ assertNull(((ArrayNode)node.get("value")).get(1));
+
+ //ADD
+ String payload = "{\n" +
+ " \"@odata.context\": \""+baseURL+"/$metadata#Collection($ref)\",\n" +
+ " \"value\": [\n" +
+ " { \"@odata.id\": \"People('russellwhyte')\" },\n" +
+ " { \"@odata.id\": \"People('scottketchum')\" } \n" +
+ " ]\n" +
+ "}";
+ response = http.POST(baseURL + "/People('kristakemp')/Friends/$ref")
+ .content(content(payload), "application/json")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ //GET
+ response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ assertEquals("/People('russellwhyte')", ((ArrayNode)node.get("value")).get(1).get("@odata.id").asText());
+ assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(2).get("@odata.id").asText());
+ }
+
+
+ @Test
+ public void testEntityId() throws Exception {
+ ContentResponse response = http.GET(baseURL+"/$entity?$id="+baseURL + "/People('kristakemp')");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+ assertEquals("kristakemp", node.get("UserName").asText());
+
+ // using relative URL
+ response = http.GET(baseURL+"/$entity?$id="+"People('kristakemp')");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+ assertEquals("kristakemp", node.get("UserName").asText());
+ }
+
+ @Test
+ public void testCreateReadDeleteEntity() throws Exception {
+ String payload = "{\n" +
+ " \"UserName\":\"olingodude\",\n" +
+ " \"FirstName\":\"Olingo\",\n" +
+ " \"LastName\":\"Apache\",\n" +
+ " \"Emails\":[\n" +
+ " \"olingo@apache.org\"\n" +
+ " ],\n" +
+ " \"AddressInfo\":[\n" +
+ " {\n" +
+ " \"Address\":\"100 apache Ln.\",\n" +
+ " \"City\":{\n" +
+ " \"CountryRegion\":\"United States\",\n" +
+ " \"Name\":\"Boise\",\n" +
+ " \"Region\":\"ID\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"Gender\":\"0\",\n" +
+ " \"Concurrency\":635585295719432047\n" +
+ "}";
+ ContentResponse response = http.POST(baseURL + "/People")
+ .content(content(payload), "application/json")
+ .header("Prefer", "return=minimal")
+ .send();
+ // the below woud be 204, if minimal was not supplied
+ assertEquals(204, response.getStatus());
+ assertEquals("/People('olingodude')", response.getHeaders().get("Location"));
+ assertEquals("return=minimal", response.getHeaders().get("Preference-Applied"));
+
+ String location = baseURL+response.getHeaders().get("Location");
+ response = http.GET(location);
+ assertEquals(200, response.getStatus());
+
+ response = http.newRequest(location).method(HttpMethod.DELETE).send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(location);
+ assertEquals(404, response.getStatus());
+ }
+
+
+ @Test
+ public void testCreateEntityWithLinkToRelatedEntities() throws Exception {
+ String payload = "{\n" +
+ " \"UserName\":\"olingo\",\n" +
+ " \"FirstName\":\"Olingo\",\n" +
+ " \"LastName\":\"Apache\",\n" +
+ " \"Emails\":[\n" +
+ " \"olingo@apache.org\"\n" +
+ " ],\n" +
+ " \"AddressInfo\":[\n" +
+ " {\n" +
+ " \"Address\":\"100 apache Ln.\",\n" +
+ " \"City\":{\n" +
+ " \"CountryRegion\":\"United States\",\n" +
+ " \"Name\":\"Boise\",\n" +
+ " \"Region\":\"ID\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"Gender\":\"0\",\n" +
+ " \"Concurrency\":635585295719432047,\n" +
+ "\"Friends@odata.bind\":[\"" +
+ baseURL+"/People('russellwhyte')\",\""+
+ baseURL+"/People('scottketchum')\""+
+ "]"+
+ "}";
+ ContentResponse response = http.POST(baseURL + "/People")
+ .content(content(payload), "application/json")
+ .header("Prefer", "return=minimal")
+ .send();
+ // the below woud be 204, if minimal was not supplied
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(baseURL+"/People('olingo')/Friends");
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("scottketchum", ((ArrayNode)node.get("value")).get(1).get("UserName").asText());
+ }
+
+ @Test
+ public void testUpdatePrimitiveProperty() throws Exception {
+ String payload = "{"
+ + " \"value\":\"Pilar Ackerman\""
+ + "}";
+
+ String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content(payload), "application/json")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/FirstName", node.get("@odata.context").asText());
+ assertEquals("Pilar Ackerman", node.get("value").asText());
+ }
+
+ @Test
+ public void testUpdatePrimitiveArrayProperty() throws Exception {
+ String payload = "{"
+ + " \"value\": [\n" +
+ " \"olingo@apache.com\"\n" +
+ " ]"
+ + "}";
+
+ String editUrl = baseURL + "/People('russellwhyte')/Emails";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content(payload), "application/json")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("olingo@apache.com", ((ArrayNode)node.get("value")).get(0).asText());
+ }
+
+ @Test
+ public void testDeleteProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("Russell", node.get("value").asText());
+
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCollection() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Friends";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("scottketchum", person.get("UserName").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCollection2() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Friends('scottketchum')/Trips";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Friends('scottketchum')/Trips",
+ node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("1001", ((ArrayNode)node.get("value")).get(0).get("TripId").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntity() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips/$entity",
+ node.get("@odata.context").asText());
+ assertEquals("f94e9116-8bdd-4dac-ab61-08438d0d9a71", node.get("ShareId").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityNotExisting() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(9999)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntitySetNotExisting() throws Exception {
+ String editUrl = baseURL + "/People('jhondoe')/Trips";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('jhondoe')/Trips",
+ node.get("@odata.context").asText());
+ assertEquals(0, ((ArrayNode)node.get("value")).size());
+ }
+
+ @Test
+ public void testBadNavigationProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Unknown";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode",
+ node.get("@odata.context").asText());
+
+ assertEquals("JH58494", node.get("value").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityMultipleDerivedTypes() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Flight",
+ ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCoolectionDerivedFilter() throws Exception {
+ String editUrl = baseURL
+ + "/People('russellwhyte')/Trips(1003)/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Event";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event",
+ ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityDerivedFilter() throws Exception {
+ String editUrl = baseURL+ "/People('russellwhyte')/Trips(1003)/PlanItems(56)/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event/$entity",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event", node.get("@odata.type").asText());
+ assertEquals("56", node.get("PlanItemId").asText());
+ }
+
+ @Test
+ public void testUpdateReference() throws Exception {
+ ContentResponse response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("/Photos(12)", node.get("@odata.id").asText());
+
+ String msg = "{\n" +
+ "\"@odata.id\": \"/Photos(11)\"\n" +
+ "}";
+ String editUrl = baseURL + "/People('ronaldmundy')/Photo/$ref";
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.PUT)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertEquals("/Photos(11)", node.get("@odata.id").asText());
+ }
+
+ @Test
+ public void testAddDelete2ReferenceCollection() throws Exception {
+ // add
+ String msg = "{\n" +
+ "\"@odata.id\": \"/People('russellwhyte')\"\n" +
+ "}";
+ String editUrl = baseURL + "/People('vincentcalabrese')/Friends/$ref";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.POST)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ // get
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("/People('russellwhyte')",
+ ((ArrayNode) node.get("value")).get(2).get("@odata.id").asText());
+
+ //delete
+ response = http.newRequest(editUrl+"?$id="+baseURL+"/People('russellwhyte')")
+ .method(HttpMethod.DELETE)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ // get
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertNull("/People('russellwhyte')", ((ArrayNode) node.get("value")).get(2));
+ }
+
+ @Test
+ public void testDeleteReference() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Photo/$ref";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testCrossJoin() throws Exception {
+ String editUrl = baseURL + "/$crossjoin(People,Airlines)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ }
+
+ public static ContentProvider content(final String msg) {
+ return new ContentProvider() {
+ boolean hasNext = true;
+
+ @Override
+ public Iterator<ByteBuffer> iterator() {
+ return new Iterator<ByteBuffer>() {
+ @Override
+ public boolean hasNext() {
+ return hasNext;
+ }
+ @Override
+ public ByteBuffer next() {
+ hasNext = false;
+ return ByteBuffer.wrap(msg.getBytes());
+ }
+ @Override
+ public void remove() {
+ }
+ };
+ }
+ @Override
+ public long getLength() {
+ return msg.length();
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
new file mode 100644
index 0000000..2c05d65
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
@@ -0,0 +1,75 @@
+/*
+ * 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.server.example;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataHttpHandler;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.core.MetadataParser;
+import org.apache.olingo.server.core.OData4Impl;
+
+public class TripPinServlet extends HttpServlet {
+ private static final long serialVersionUID = 2663595419366214401L;
+ private TripPinDataModel dataModel;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ }
+
+ @Override
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ OData odata = OData4Impl.newInstance();
+ MetadataParser parser = new MetadataParser();
+ EdmProvider edmProvider = null;
+
+ try {
+ edmProvider = parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
+ } catch (XMLStreamException e) {
+ throw new IOException(e);
+ }
+
+ ServiceMetadata metadata = odata.createServiceMetadata(edmProvider, Collections.EMPTY_LIST);
+
+ ODataHttpHandler handler = odata.createHandler(metadata);
+
+ if (this.dataModel == null) {
+ try {
+ this.dataModel = new TripPinDataModel(metadata);
+ } catch (Exception e) {
+ throw new IOException("Failed to load data for TripPin Service");
+ }
+ }
+
+ handler.register(new TripPinHandler(this.dataModel));
+ handler.process(request, response);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png
new file mode 100644
index 0000000..d614f1a
Binary files /dev/null and b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png differ
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/airlines.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/airlines.json b/lib/server-core-ext/src/test/resources/airlines.json
new file mode 100644
index 0000000..78eccdc
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airlines.json
@@ -0,0 +1,64 @@
+{
+ "value":[
+ {
+ "AirlineCode":"AA",
+ "Name":"American Airlines"
+ },
+ {
+ "AirlineCode":"FM",
+ "Name":"Shanghai Airline"
+ },
+ {
+ "AirlineCode":"MU",
+ "Name":"China Eastern Airlines"
+ },
+ {
+ "AirlineCode":"AF",
+ "Name":"Air France"
+ },
+ {
+ "AirlineCode":"AZ",
+ "Name":"Alitalia"
+ },
+ {
+ "AirlineCode":"AC",
+ "Name":"Air Canada"
+ },
+ {
+ "AirlineCode":"OS",
+ "Name":"Austrian Airlines"
+ },
+ {
+ "AirlineCode":"TK",
+ "Name":"Turkish Airlines"
+ },
+ {
+ "AirlineCode":"JL",
+ "Name":"Japan Airlines"
+ },
+ {
+ "AirlineCode":"SQ",
+ "Name":"Singapore Airlines"
+ },
+ {
+ "AirlineCode":"KE",
+ "Name":"Korean Air"
+ },
+ {
+ "AirlineCode":"CZ",
+ "Name":"China Southern"
+ },
+ {
+ "AirlineCode":"AK",
+ "Name":"AirAsia"
+ },
+ {
+ "AirlineCode":"HX",
+ "Name":"Hong Kong Airlines"
+ },
+ {
+ "AirlineCode":"EK",
+ "Name":"Emirates"
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/airports.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/airports.json b/lib/server-core-ext/src/test/resources/airports.json
new file mode 100644
index 0000000..c06a5cf
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airports.json
@@ -0,0 +1,394 @@
+{
+ "value":[
+ {
+ "IcaoCode":"KSFO",
+ "Name":"San Francisco International Airport",
+ "IataCode":"SFO",
+ "Location":{
+ "Address":"South McDonnell Road, San Francisco, CA 94128",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"San Francisco",
+ "Region":"California"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -122.374722222222,
+ 37.6188888888889
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KLAX",
+ "Name":"Los Angeles International Airport",
+ "IataCode":"LAX",
+ "Location":{
+ "Address":"1 World Way, Los Angeles, CA, 90045",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Los Angeles",
+ "Region":"California"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -118.408055555556,
+ 33.9425
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZSSS",
+ "Name":"Shanghai Hongqiao International Airport",
+ "IataCode":"SHA",
+ "Location":{
+ "Address":"Hongqiao Road 2550, Changning District",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Shanghai",
+ "Region":"Shanghai"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 121.336111111111,
+ 31.1977777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZBAA",
+ "Name":"Beijing Capital International Airport",
+ "IataCode":"PEK",
+ "Location":{
+ "Address":"Airport Road, Chaoyang District, Beijing, 100621",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Beijing",
+ "Region":"Beijing"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 116.584444444444,
+ 40.08
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KJFK",
+ "Name":"John F. Kennedy International Airport",
+ "IataCode":"JFK",
+ "Location":{
+ "Address":"Jamaica, New York, NY 11430",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"New York City",
+ "Region":"New York"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -73.7788888888889,
+ 40.6397222222222
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"LIRA",
+ "Name":"Rome Ciampino Airport",
+ "IataCode":"CIA",
+ "Location":{
+ "Address":"Via Appia Nuova, 1651",
+ "City":{
+ "CountryRegion":"Italy",
+ "Name":"Rome",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 12.5947222222222,
+ 41.7991666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"CYYZ",
+ "Name":"Toronto Pearson International Airport",
+ "IataCode":"YYZ",
+ "Location":{
+ "Address":"6301 Silver Dart Dr Mississauga",
+ "City":{
+ "CountryRegion":"Canada",
+ "Name":"Mississauga",
+ "Region":"Ontario"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -79.6305555555555,
+ 43.6772222222222
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"YSSY",
+ "Name":"Sydney Airport",
+ "IataCode":"SYD",
+ "Location":{
+ "Address":"Airport Dr Sydney NSW 2020",
+ "City":{
+ "CountryRegion":"Australia",
+ "Name":"Sydney",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 151.177222222222,
+ -33.9461111111111
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"LTBA",
+ "Name":"Istanbul Ataturk Airport",
+ "IataCode":"IST",
+ "Location":{
+ "Address":"Ye\u015filk\u00f6y Mh.34149 \u0130stanbul",
+ "City":{
+ "CountryRegion":"Turkey",
+ "Name":"Istanbul",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 28.8211111111111,
+ 40.9766666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"WSSS",
+ "Name":"Singapore Changi Airport",
+ "IataCode":"SIN",
+ "Location":{
+ "Address":"Airport Blvd, Singapore",
+ "City":{
+ "CountryRegion":"Singapore",
+ "Name":"Changi",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 103.987222222222,
+ 1.35555555555556
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"OMAA",
+ "Name":"Abu Dhabi International Airport",
+ "IataCode":"AUH",
+ "Location":{
+ "Address":"Sheik Maktoum Bin Rashid Rd Abu Dhabi",
+ "City":{
+ "CountryRegion":"United Arab Emirates",
+ "Name":"Abu Dhabi",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 54.6511111111111,
+ 24.4327777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZGGG",
+ "Name":"Guangzhou Baiyun International Airport",
+ "IataCode":"CAN",
+ "Location":{
+ "Address":"Jichang Road, Renhezhen, Huadu",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Guangzhou",
+ "Region":"Guangdong"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 113.265833333333,
+ 23.1841666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KORD",
+ "Name":"O'Hare International Airport",
+ "IataCode":"ORD",
+ "Location":{
+ "Address":"10000 W O'Hare Ave",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Chicago",
+ "Region":"Illinois"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -87.9044444444445,
+ 41.9794444444444
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KATL",
+ "Name":"Hartsfield-Jackson Atlanta International Airport",
+ "IataCode":"ATL",
+ "Location":{
+ "Address":"6000 N Terminal Pkwy",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Atlanta",
+ "Region":"Georgia"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -84.4269444444444,
+ 33.6402777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KSEA",
+ "Name":"Seattle-Tacoma International Airport",
+ "IataCode":"SEA",
+ "Location":{
+ "Address":"17801 International Blvd",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"SeaTac",
+ "Region":"Washington"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -122.309166666667,
+ 47.4488888888889
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/event.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/event.json b/lib/server-core-ext/src/test/resources/event.json
new file mode 100644
index 0000000..eb19dde
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/event.json
@@ -0,0 +1,157 @@
+{
+ "value":[
+ {
+ "PlanItemId": 50,
+ "Description": "Client Meeting",
+ "ConfirmationCode": "4372899DD",
+ "StartsAt": "2014-01-02T13:00:00Z",
+ "EndsAt": "2014-01-02T16:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Regus Business Center",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "100 Church Street, 8th Floor, Manhattan, 10007"
+ }
+ },
+ {
+ "PlanItemId": 51,
+ "Description": "Visit the Brooklyn Bridge Park",
+ "ConfirmationCode": "4372899AA",
+ "StartsAt": "2014-01-01T15:00:00Z",
+ "EndsAt": "2014-01-01T16:00:00Z",
+ "Duration": "PT1H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Brooklyn Bridge Park, at Fulton Ferry Landing",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Main St Dumbo Brooklyn 11201"
+ }
+ },
+ {
+ "PlanItemId": 52,
+ "Description": "Empire State Building",
+ "ConfirmationCode": "4372899BB",
+ "StartsAt": "2014-01-03T10:00:00Z",
+ "EndsAt": "2014-01-03T12:00:00Z",
+ "Duration": "PT2H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Empire State Building",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Empire State Building, 350 5th Ave"
+ }
+ },
+ {
+ "PlanItemId": 53,
+ "Description": "Coney Island",
+ "ConfirmationCode": "4372899CC",
+ "StartsAt": "2014-01-03T14:00:00Z",
+ "EndsAt": "2014-01-03T20:00:00Z",
+ "Duration": "PT6H",
+ "OccursAt":
+ {
+ "BuildingInfo": "",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "1208 Surf Ave, Brooklyn"
+ }
+ },
+ {
+ "PlanItemId": 54,
+ "Description": "Shopping at Times Square",
+ "ConfirmationCode": "4372899DD",
+ "StartsAt": "2014-01-04T10:00:00Z",
+ "EndsAt": "2014-01-04T15:00:00Z",
+ "Duration": "PT5H",
+ "OccursAt":
+ {
+ "BuildingInfo": "",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Broadway, 7th Avenue, 42nd and 47th Streets"
+ }
+ },
+ {
+ "PlanItemId": 55,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899EE",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "Address": "10 Beijing Street, 100000",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "BuildingInfo": "Beijing Restaurant"
+ }
+ },
+ {
+ "PlanItemId": 56,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899FF",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Beijing Restaurant",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "Address": "10 Beijing Street, 100000"
+ }
+ },
+ {
+ "PlanItemId": 57,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899GG",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Beijing Restaurant",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "Address": "10 Beijing Street, 100000"
+ }
+ }
+ ]
+}
+
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/flight-links.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/flight-links.json b/lib/server-core-ext/src/test/resources/flight-links.json
new file mode 100644
index 0000000..17741d0
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight-links.json
@@ -0,0 +1,52 @@
+{
+ "value":[
+ {
+ "PlanItemId": 1,
+ "Airline": "AA",
+ "From": "ORD",
+ "To": "JFK"
+ },
+ {
+ "PlanItemId": 2,
+ "Airline": "AA",
+ "From": "JFK",
+ "To": "ORD"
+ },
+ {
+ "PlanItemId": 3,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 4,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ },
+ {
+ "PlanItemId": 5,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 6,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ },
+ {
+ "PlanItemId": 7,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 8,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/flight.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/flight.json b/lib/server-core-ext/src/test/resources/flight.json
new file mode 100644
index 0000000..af06998
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight.json
@@ -0,0 +1,66 @@
+{
+ "value":[
+ {
+ "PlanItemId": 1,
+ "ConfirmationCode": "JH58493",
+ "FlightNumber": "AA26",
+ "StartsAt": "2014-01-01T06:15:00Z",
+ "EndsAt": "2014-01-01T11:35:00Z"
+ },
+ {
+ "PlanItemId": 2,
+ "ConfirmationCode": "JH38143",
+ "FlightNumber": "AA4035",
+ "StartsAt": "2014-01-04T17:55:00Z",
+ "EndsAt": "2014-01-04T20:45:00Z"
+ },
+ {
+ "PlanItemId": 3,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B11"
+ },
+ {
+ "PlanItemId": 4,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T15:00:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A32"
+ },
+ {
+ "PlanItemId": 5,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B11"
+ },
+ {
+ "PlanItemId": 6,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T15:00:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A32"
+ },
+ {
+ "PlanItemId": 7,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B12"
+ },
+ {
+ "PlanItemId": 8,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T16:30:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A33"
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/people-links.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/people-links.json b/lib/server-core-ext/src/test/resources/people-links.json
new file mode 100644
index 0000000..878d6ce
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/people-links.json
@@ -0,0 +1,94 @@
+{
+ "value":[
+ {
+ "UserName":"russellwhyte",
+ "Friends": ["scottketchum", "ronaldmundy", "javieralfred", "angelhuffman"],
+ "Trips": [1001, 1003, 1007],
+ "Photo": 1
+ },
+ {
+ "UserName":"scottketchum",
+ "Friends": ["russellwhyte", "ronaldmundy"],
+ "Trips": [1001, 2004],
+ "Photo": 11
+ },
+ {
+ "UserName":"ronaldmundy",
+ "Friends": ["russellwhyte", "scottketchum"],
+ "Trips": [3009],
+ "Photo": 12
+ },
+ {
+ "UserName":"javieralfred",
+ "Friends": ["willieashmore", "vincentcalabrese", "georginabarlow"],
+ "Trips": [4005]
+ },
+ {
+ "UserName":"willieashmore",
+ "Friends": ["javieralfred", "vincentcalabrese"],
+ "Trips": [5007, 5008]
+ },
+ {
+ "UserName":"vincentcalabrese",
+ "Friends": ["javieralfred", "willieashmore"],
+ "Trips": [7010]
+ },
+ {
+ "UserName":"clydeguess",
+ "Friends": ["keithpinckney", "ursulabright"],
+ "Trips": [8011]
+ },
+ {
+ "UserName":"keithpinckney",
+ "Friends": ["clydeguess", "marshallgaray"],
+ "Trips": []
+ },
+ {
+ "UserName":"marshallgaray",
+ "Friends": ["keithpinckney", "elainestewart", "jonirosales"]
+ },
+ {
+ "UserName":"elainestewart",
+ "Friends": ["marshallgaray"]
+ },
+ {
+ "UserName":"salliesampson",
+ "Friends": [""],
+ "Trips": [13012]
+ },
+ {
+ "UserName":"jonirosales",
+ "Friends": ["marshallgaray"],
+ "Trips": [14013]
+ },
+ {
+ "UserName":"georginabarlow",
+ "Friends": ["javieralfred"]
+ },
+ {
+ "UserName":"angelhuffman",
+ "Friends": ["russellwhyte"],
+ "Trips": [16014]
+ },
+ {
+ "UserName":"laurelosborn",
+ "Friends": ["sandyosborn"]
+ },
+ {
+ "UserName":"sandyosborn",
+ "Friends": ["laurelosborn"]
+ },
+ {
+ "UserName":"ursulabright",
+ "Friends": ["keithpinckney"]
+ },
+ {
+ "UserName":"genevievereeves",
+ "Friends": ["kristakemp"]
+ },
+ {
+ "UserName":"kristakemp",
+ "Friends": ["genevievereeves"]
+ }
+ ]
+}