blob: d3b89d72adf02e99b0087b0d3bde016141ccd15b [file] [log] [blame]
/*
* 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.camel.component.olingo4;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.camel.component.olingo4.api.Olingo4App;
import org.apache.camel.component.olingo4.api.Olingo4ResponseHandler;
import org.apache.camel.component.olingo4.api.batch.Olingo4BatchChangeRequest;
import org.apache.camel.component.olingo4.api.batch.Olingo4BatchQueryRequest;
import org.apache.camel.component.olingo4.api.batch.Olingo4BatchRequest;
import org.apache.camel.component.olingo4.api.batch.Olingo4BatchResponse;
import org.apache.camel.component.olingo4.api.batch.Operation;
import org.apache.camel.component.olingo4.api.impl.Olingo4AppImpl;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.domain.ClientCollectionValue;
import org.apache.olingo.client.api.domain.ClientComplexValue;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.domain.ClientEnumValue;
import org.apache.olingo.client.api.domain.ClientObjectFactory;
import org.apache.olingo.client.api.domain.ClientPrimitiveValue;
import org.apache.olingo.client.api.domain.ClientProperty;
import org.apache.olingo.client.api.domain.ClientServiceDocument;
import org.apache.olingo.client.api.domain.ClientValue;
import org.apache.olingo.client.api.serialization.ODataReader;
import org.apache.olingo.client.api.serialization.ODataSerializerException;
import org.apache.olingo.client.api.serialization.ODataWriter;
import org.apache.olingo.client.core.ODataClientFactory;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
/**
* Integration test for
* {@link org.apache.camel.component.olingo4.api.impl.Olingo4AppImpl} using the
* sample OData 4.0 remote TripPin service published on
* http://services.odata.org/TripPinRESTierService.
*/
public class Olingo4AppAPITest {
private static final Logger LOG = LoggerFactory.getLogger(Olingo4AppAPITest.class);
private static final long TIMEOUT = 10;
private static final String PEOPLE = "People";
private static final String TEST_PEOPLE = "People('russellwhyte')";
private static final String TEST_AIRLINE = "Airlines('FM')";
private static final String TEST_AIRLINE_TO_UPDATE = "Airlines('AA')"; // Careful
// using
// this
// as
// it
// get
// updated!
private static final String TEST_AIRLINE_TO_DELETE = "Airlines('MU')"; // Careful
// using
// this
// as
// it
// gets
// deleted!
private static final String TRIPS = "Trips";
private static final String TEST_CREATE_RESOURCE_CONTENT_ID = "1";
private static final String TEST_UPDATE_RESOURCE_CONTENT_ID = "2";
private static final String TEST_CREATE_KEY = "'lewisblack'";
private static final String TEST_CREATE_PEOPLE = PEOPLE + "(" + TEST_CREATE_KEY + ")";
private static final String TEST_AIRPORT = "Airports('KSFO')";
private static final String TEST_AIRPORTS_SIMPLE_PROPERTY = TEST_AIRPORT + "/Name";
private static final String TEST_AIRPORTS_COMPLEX_PROPERTY = TEST_AIRPORT + "/Location";
private static final String TEST_AIRPORTS_SIMPLE_PROPERTY_VALUE = TEST_AIRPORTS_SIMPLE_PROPERTY + "/$value";
private static final String COUNT_OPTION = "/$count";
private static final String TEST_UNBOUND_ACTION_RESETDATASOURCE = "ResetDataSource";
private static final String TEST_BOUND_ACTION_PEOPLE_SHARETRIP = TEST_PEOPLE + "/Microsoft.OData.Service.Sample.TrippinInMemory.Models.ShareTrip";
private static final String TEST_SERVICE_BASE_URL = "http://services.odata.org/TripPinRESTierService";
private static final ContentType TEST_FORMAT = ContentType.APPLICATION_JSON;
private static final String TEST_FORMAT_STRING = TEST_FORMAT.toString();
private static Olingo4App olingoApp;
private static Edm edm;
private final ODataClient odataClient = ODataClientFactory.getClient();
private final ClientObjectFactory objFactory = odataClient.getObjectFactory();
private final ODataReader reader = odataClient.getReader();
@BeforeClass
public static void beforeClass() throws Exception {
setupClient();
}
@AfterClass
public static void afterClass() throws Exception {
if (olingoApp != null) {
olingoApp.close();
}
}
protected static void setupClient() throws Exception {
olingoApp = new Olingo4AppImpl(getRealServiceUrl(TEST_SERVICE_BASE_URL));
olingoApp.setContentType(TEST_FORMAT_STRING);
LOG.info("Read Edm ");
final TestOlingo4ResponseHandler<Edm> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(null, Constants.METADATA, null, null, responseHandler);
edm = responseHandler.await();
LOG.info("Read default EntityContainer: {}", responseHandler.await().getEntityContainer().getName());
}
/*
* Every request to the demo OData 4.0
* (http://services.odata.org/TripPinRESTierService) generates unique
* service URL with postfix like (S(tuivu3up5ygvjzo5fszvnwfv)) for each
* session This method makes reuest to the base URL and return URL with
* generated postfix
*/
@SuppressWarnings("deprecation")
protected static String getRealServiceUrl(String baseUrl) throws ClientProtocolException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(baseUrl);
HttpContext httpContext = new BasicHttpContext();
httpclient.execute(httpGet, httpContext);
HttpUriRequest currentReq = (HttpUriRequest)httpContext.getAttribute(ExecutionContext.HTTP_REQUEST);
HttpHost currentHost = (HttpHost)httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
String currentUrl = (currentReq.getURI().isAbsolute()) ? currentReq.getURI().toString() : (currentHost.toURI() + currentReq.getURI());
return currentUrl;
}
@Test
public void testServiceDocument() throws Exception {
final TestOlingo4ResponseHandler<ClientServiceDocument> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(null, "", null, null, responseHandler);
final ClientServiceDocument serviceDocument = responseHandler.await();
final Map<String, URI> entitySets = serviceDocument.getEntitySets();
assertEquals("Service Entity Sets", 4, entitySets.size());
LOG.info("Service Document Entries: {}", entitySets);
}
@Test
public void testReadEntitySet() throws Exception {
final TestOlingo4ResponseHandler<ClientEntitySet> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, PEOPLE, null, null, responseHandler);
final ClientEntitySet entitySet = responseHandler.await();
assertNotNull(entitySet);
assertEquals("Entity set count", 20, entitySet.getEntities().size());
LOG.info("Entities: {}", prettyPrint(entitySet));
}
@Test
public void testReadUnparsedEntitySet() throws Exception {
final TestOlingo4ResponseHandler<InputStream> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.uread(edm, PEOPLE, null, null, responseHandler);
final InputStream rawEntitySet = responseHandler.await();
assertNotNull("Data entity set", rawEntitySet);
final ClientEntitySet entitySet = reader.readEntitySet(rawEntitySet, TEST_FORMAT);
assertEquals("Entity set count", 20, entitySet.getEntities().size());
LOG.info("Entries: {}", prettyPrint(entitySet));
}
@Test
public void testReadEntity() throws Exception {
final TestOlingo4ResponseHandler<ClientEntity> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE, null, null, responseHandler);
ClientEntity entity = responseHandler.await();
assertEquals("Shanghai Airline", entity.getProperty("Name").getValue().toString());
LOG.info("Single Entity: {}", prettyPrint(entity));
responseHandler.reset();
olingoApp.read(edm, TEST_PEOPLE, null, null, responseHandler);
entity = responseHandler.await();
assertEquals("Russell", entity.getProperty("FirstName").getValue().toString());
LOG.info("Single Entry: {}", prettyPrint(entity));
responseHandler.reset();
final Map<String, String> queryParams = new HashMap<>();
queryParams.put(SystemQueryOptionKind.EXPAND.toString(), TRIPS);
olingoApp.read(edm, TEST_PEOPLE, queryParams, null, responseHandler);
ClientEntity entityExpanded = responseHandler.await();
LOG.info("Single People Entiry with expanded Trips relation: {}", prettyPrint(entityExpanded));
}
@Test
public void testReadUnparsedEntity() throws Exception {
final TestOlingo4ResponseHandler<InputStream> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.uread(edm, TEST_AIRLINE, null, null, responseHandler);
InputStream rawEntity = responseHandler.await();
assertNotNull("Data entity", rawEntity);
ClientEntity entity = reader.readEntity(rawEntity, TEST_FORMAT);
assertEquals("Shanghai Airline", entity.getProperty("Name").getValue().toString());
LOG.info("Single Entity: {}", prettyPrint(entity));
responseHandler.reset();
olingoApp.uread(edm, TEST_PEOPLE, null, null, responseHandler);
rawEntity = responseHandler.await();
entity = reader.readEntity(rawEntity, TEST_FORMAT);
assertEquals("Russell", entity.getProperty("FirstName").getValue().toString());
LOG.info("Single Entity: {}", prettyPrint(entity));
responseHandler.reset();
final Map<String, String> queryParams = new HashMap<>();
queryParams.put(SystemQueryOptionKind.EXPAND.toString(), TRIPS);
olingoApp.uread(edm, TEST_PEOPLE, queryParams, null, responseHandler);
rawEntity = responseHandler.await();
entity = reader.readEntity(rawEntity, TEST_FORMAT);
LOG.info("Single People Entiry with expanded Trips relation: {}", prettyPrint(entity));
}
@Test
public void testReadUpdateProperties() throws Exception {
// test simple property Airports.Name
final TestOlingo4ResponseHandler<ClientPrimitiveValue> propertyHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRPORTS_SIMPLE_PROPERTY, null, null, propertyHandler);
ClientPrimitiveValue name = propertyHandler.await();
assertEquals("San Francisco International Airport", name.toString());
LOG.info("Airport name property value {}", name.asPrimitive());
final TestOlingo4ResponseHandler<ClientPrimitiveValue> valueHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRPORTS_SIMPLE_PROPERTY_VALUE, null, null, valueHandler);
ClientPrimitiveValue nameValue = valueHandler.await();
assertEquals("San Francisco International Airport", name.toString());
LOG.info("Airport name property value {}", nameValue);
TestOlingo4ResponseHandler<HttpStatusCode> statusHandler = new TestOlingo4ResponseHandler<>();
// All properties updates (simple and complex) are performing through
// ClientEntity object
ClientEntity clientEntity = objFactory.newEntity(null);
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("MiddleName", objFactory.newPrimitiveValueBuilder().buildString("Middle")));
olingoApp.update(edm, TEST_PEOPLE, null, clientEntity, statusHandler);
HttpStatusCode statusCode = statusHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
LOG.info("Name property updated with status {}", statusCode.getStatusCode());
// Check for updated property by reading entire entity
final TestOlingo4ResponseHandler<ClientEntity> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_PEOPLE, null, null, responseHandler);
ClientEntity entity = responseHandler.await();
assertEquals("Middle", entity.getProperty("MiddleName").getValue().toString());
LOG.info("Updated Single Entity: {}", prettyPrint(entity));
}
@Test
public void testReadCount() throws Exception {
final TestOlingo4ResponseHandler<Long> countHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, PEOPLE + COUNT_OPTION, null, null, countHandler);
Long count = countHandler.await();
assertEquals(20, count.intValue());
LOG.info("People count: {}", count);
}
/**
* The Airline resource is implemented with Optimistic Concurrency. This
* requires an eTag to be first fetched via a read before performing patch,
* update, delete or merge operations. The test should complete successfully
* and not throw an error of the form 'The request need to have If-Match or
* If-None-Match header'
*
* @throws Exception
*/
@Test
public void testDeleteOptimisticConcurrency() throws Exception {
// test simple property Airlines
final TestOlingo4ResponseHandler<ClientEntity> entityHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_DELETE, null, null, entityHandler);
// Confirm presence of eTag
ClientEntity airline = entityHandler.await();
assertNotNull(airline);
assertNotNull(airline.getETag());
TestOlingo4ResponseHandler<HttpStatusCode> statusHandler = new TestOlingo4ResponseHandler<>();
//
// Call delete
//
olingoApp.delete(TEST_AIRLINE_TO_DELETE, null, statusHandler);
HttpStatusCode statusCode = statusHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
LOG.info("Deleted entity at {}", TEST_AIRLINE_TO_DELETE);
// Check for deleted entity
final TestOlingo4ResponseHandler<HttpStatusCode> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_DELETE, null, null, responseHandler);
statusCode = statusHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
LOG.info("Deleted entity at {}", TEST_AIRLINE_TO_DELETE);
}
/**
* The Airline resource is implemented with Optimistic Concurrency. This
* requires an eTag to be first fetched via a read before performing patch,
* update, delete or merge operations. The test should complete successfully
* and not throw an error of the form 'The request need to have If-Match or
* If-None-Match header'
*
* @throws Exception
*/
@Test
public void testPatchOptimisticConcurrency() throws Exception {
// test simple property Airlines
final TestOlingo4ResponseHandler<ClientEntity> entityHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_UPDATE, null, null, entityHandler);
// Confirm presence of eTag
ClientEntity airline = entityHandler.await();
assertNotNull(airline);
assertNotNull(airline.getETag());
TestOlingo4ResponseHandler<HttpStatusCode> statusHandler = new TestOlingo4ResponseHandler<>();
ClientEntity clientEntity = objFactory.newEntity(null);
String newAirlineName = "The Patched American Airlines";
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("Name", objFactory.newPrimitiveValueBuilder().buildString(newAirlineName)));
//
// Call patch
//
olingoApp.patch(edm, TEST_AIRLINE_TO_UPDATE, null, clientEntity, statusHandler);
HttpStatusCode statusCode = statusHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
LOG.info("Name property updated with status {}", statusCode.getStatusCode());
// Check for updated entity
final TestOlingo4ResponseHandler<ClientEntity> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_UPDATE, null, null, responseHandler);
ClientEntity entity = responseHandler.await();
assertEquals(newAirlineName, entity.getProperty("Name").getValue().toString());
LOG.info("Updated Single Entity: {}", prettyPrint(entity));
}
/**
* The Airline resource is implemented with Optimistic Concurrency. This
* requires an eTag to be first fetched via a read before performing patch,
* update, delete or merge operations. The test should complete successfully
* and not throw an error of the form 'The request need to have If-Match or
* If-None-Match header'
*
* @throws Exception
*/
@Test
public void testUpdateOptimisticConcurrency() throws Exception {
// test simple property Airlines
final TestOlingo4ResponseHandler<ClientEntity> entityHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_UPDATE, null, null, entityHandler);
// Confirm presence of eTag
ClientEntity airline = entityHandler.await();
assertNotNull(airline);
assertNotNull(airline.getETag());
TestOlingo4ResponseHandler<HttpStatusCode> statusHandler = new TestOlingo4ResponseHandler<>();
ClientEntity clientEntity = objFactory.newEntity(null);
String newAirlineName = "The Updated American Airlines";
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("Name", objFactory.newPrimitiveValueBuilder().buildString(newAirlineName)));
//
// Call update
//
olingoApp.update(edm, TEST_AIRLINE_TO_UPDATE, null, clientEntity, statusHandler);
HttpStatusCode statusCode = statusHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
LOG.info("Name property updated with status {}", statusCode.getStatusCode());
// Check for updated entity
final TestOlingo4ResponseHandler<ClientEntity> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(edm, TEST_AIRLINE_TO_UPDATE, null, null, responseHandler);
ClientEntity entity = responseHandler.await();
assertEquals(newAirlineName, entity.getProperty("Name").getValue().toString());
LOG.info("Updated Single Entity: {}", prettyPrint(entity));
}
@Test
public void testCreateUpdateDeleteEntity() throws Exception {
// create an entity to update
final TestOlingo4ResponseHandler<ClientEntity> entryHandler = new TestOlingo4ResponseHandler<>();
olingoApp.create(edm, PEOPLE, null, createEntity(), entryHandler);
ClientEntity createdEntity = entryHandler.await();
LOG.info("Created Entity: {}", prettyPrint(createdEntity));
final TestOlingo4ResponseHandler<HttpStatusCode> statusHandler = new TestOlingo4ResponseHandler<>();
ClientEntity updateEntity = createEntity();
updateEntity.getProperties().add(objFactory.newPrimitiveProperty("MiddleName", objFactory.newPrimitiveValueBuilder().buildString("Middle")));
olingoApp.update(edm, TEST_CREATE_PEOPLE, null, updateEntity, statusHandler);
statusHandler.await();
statusHandler.reset();
updateEntity = createEntity();
updateEntity.getProperties().add(objFactory.newPrimitiveProperty("MiddleName", objFactory.newPrimitiveValueBuilder().buildString("Middle Patched")));
olingoApp.patch(edm, TEST_CREATE_PEOPLE, null, updateEntity, statusHandler);
statusHandler.await();
entryHandler.reset();
olingoApp.read(edm, TEST_CREATE_PEOPLE, null, null, entryHandler);
ClientEntity updatedEntity = entryHandler.await();
LOG.info("Updated Entity successfully: {}", prettyPrint(updatedEntity));
statusHandler.reset();
olingoApp.delete(TEST_CREATE_PEOPLE, null, statusHandler);
HttpStatusCode statusCode = statusHandler.await();
LOG.info("Deletion of Entity was successful: {}: {}", statusCode.getStatusCode(), statusCode.getInfo());
try {
LOG.info("Verify Delete Entity");
entryHandler.reset();
olingoApp.read(edm, TEST_CREATE_PEOPLE, null, null, entryHandler);
entryHandler.await();
fail("Entity not deleted!");
} catch (Exception e) {
LOG.info("Deleted entity not found: {}", e.getMessage());
}
}
@Test
public void testBatchRequest() throws Exception {
final List<Olingo4BatchRequest> batchParts = new ArrayList<>();
// 1. Edm query
batchParts.add(Olingo4BatchQueryRequest.resourcePath(Constants.METADATA).resourceUri(TEST_SERVICE_BASE_URL).build());
// 2. Query entity set
batchParts.add(Olingo4BatchQueryRequest.resourcePath(PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).build());
// 3. Read entity
batchParts.add(Olingo4BatchQueryRequest.resourcePath(TEST_PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).build());
// 4. Read with expand
final HashMap<String, String> queryParams = new HashMap<>();
queryParams.put(SystemQueryOptionKind.EXPAND.toString(), TRIPS);
batchParts.add(Olingo4BatchQueryRequest.resourcePath(TEST_PEOPLE).queryParams(queryParams).resourceUri(TEST_SERVICE_BASE_URL).build());
// 5. Create entity
final ClientEntity clientEntity = createEntity();
batchParts.add(Olingo4BatchChangeRequest.resourcePath(PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).contentId(TEST_CREATE_RESOURCE_CONTENT_ID).operation(Operation.CREATE)
.body(clientEntity).build());
// 6. Update entity
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("MiddleName", objFactory.newPrimitiveValueBuilder().buildString("Lewis")));
batchParts.add(Olingo4BatchChangeRequest.resourcePath(TEST_CREATE_PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).contentId(TEST_UPDATE_RESOURCE_CONTENT_ID)
.operation(Operation.UPDATE).body(clientEntity).build());
// 7. Delete entity
batchParts.add(Olingo4BatchChangeRequest.resourcePath(TEST_CREATE_PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).operation(Operation.DELETE).build());
// 8. Read to verify entity delete
batchParts.add(Olingo4BatchQueryRequest.resourcePath(TEST_CREATE_PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).build());
final TestOlingo4ResponseHandler<List<Olingo4BatchResponse>> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.batch(edm, null, batchParts, responseHandler);
final List<Olingo4BatchResponse> responseParts = responseHandler.await(15, TimeUnit.MINUTES);
assertEquals("Batch responses expected", 8, responseParts.size());
assertNotNull(responseParts.get(0).getBody());
final ClientEntitySet clientEntitySet = (ClientEntitySet)responseParts.get(1).getBody();
assertNotNull(clientEntitySet);
LOG.info("Batch entity set: {}", prettyPrint(clientEntitySet));
ClientEntity returnClientEntity = (ClientEntity)responseParts.get(2).getBody();
assertNotNull(returnClientEntity);
LOG.info("Batch read entity: {}", prettyPrint(returnClientEntity));
returnClientEntity = (ClientEntity)responseParts.get(3).getBody();
assertNotNull(returnClientEntity);
LOG.info("Batch read entity with expand: {}", prettyPrint(returnClientEntity));
ClientEntity createdClientEntity = (ClientEntity)responseParts.get(4).getBody();
assertNotNull(createdClientEntity);
assertEquals(TEST_CREATE_RESOURCE_CONTENT_ID, responseParts.get(4).getContentId());
LOG.info("Batch created entity: {}", prettyPrint(returnClientEntity));
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), responseParts.get(5).getStatusCode());
assertEquals(TEST_UPDATE_RESOURCE_CONTENT_ID, responseParts.get(5).getContentId());
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), responseParts.get(6).getStatusCode());
assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), responseParts.get(7).getStatusCode());
}
@Test
public void testUnboundActionRequest() throws Exception {
final TestOlingo4ResponseHandler<HttpStatusCode> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.action(edm, TEST_UNBOUND_ACTION_RESETDATASOURCE, null, null, responseHandler);
final HttpStatusCode statusCode = responseHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
}
@Test
public void testBoundActionRequest() throws Exception {
final ClientEntity clientEntity = objFactory.newEntity(null);
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("userName", objFactory.newPrimitiveValueBuilder().buildString("scottketchum")));
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("tripId", objFactory.newPrimitiveValueBuilder().buildInt32(0)));
final TestOlingo4ResponseHandler<HttpStatusCode> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.action(edm, TEST_BOUND_ACTION_PEOPLE_SHARETRIP, null, clientEntity, responseHandler);
final HttpStatusCode statusCode = responseHandler.await();
assertEquals(HttpStatusCode.NO_CONTENT, statusCode);
}
// Unfortunately there is no action that returns a client entity. So we fake
// one
@Test
public void testBoundActionRequestWithClientEntityResponse() throws Exception {
final ODataClient odataClient = ODataClientFactory.getClient();
final ODataWriter odataWriter = odataClient.getWriter();
final HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.addInterceptorFirst(new HttpResponseInterceptor() {
@Override
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
if (response.getStatusLine().getStatusCode() == HttpStatusCode.NO_CONTENT.getStatusCode()) {
try {
response.setEntity(new InputStreamEntity(odataWriter.writeEntity(createEntity(), ContentType.JSON),
org.apache.http.entity.ContentType.parse(ContentType.JSON.toContentTypeString())));
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
} catch (ODataSerializerException e) {
throw new IOException(e);
}
}
}
});
final Olingo4App olingoApp = new Olingo4AppImpl(getRealServiceUrl(TEST_SERVICE_BASE_URL), httpClientBuilder);
olingoApp.setContentType(TEST_FORMAT_STRING);
final TestOlingo4ResponseHandler<Edm> responseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.read(null, Constants.METADATA, null, null, responseHandler);
final Edm edm = responseHandler.await();
final ClientEntity clientEntity = objFactory.newEntity(null);
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("userName", objFactory.newPrimitiveValueBuilder().buildString("scottketchum")));
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("tripId", objFactory.newPrimitiveValueBuilder().buildInt32(0)));
final TestOlingo4ResponseHandler<ClientEntity> actionResponseHandler = new TestOlingo4ResponseHandler<>();
olingoApp.action(edm, TEST_BOUND_ACTION_PEOPLE_SHARETRIP, null, clientEntity, actionResponseHandler);
final ClientEntity result = actionResponseHandler.await();
assertEquals("lewisblack", result.getProperty("UserName").getValue().toString());
}
private ClientEntity createEntity() {
ClientEntity clientEntity = objFactory.newEntity(null);
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("UserName", objFactory.newPrimitiveValueBuilder().buildString("lewisblack")));
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("FirstName", objFactory.newPrimitiveValueBuilder().buildString("Lewis")));
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("LastName", objFactory.newPrimitiveValueBuilder().buildString("Black")));
return clientEntity;
}
private static String prettyPrint(ClientEntitySet entitySet) {
StringBuilder builder = new StringBuilder();
builder.append("[\n");
for (ClientEntity entity : entitySet.getEntities()) {
builder.append(prettyPrint(entity.getProperties(), 1)).append('\n');
}
builder.append("]\n");
return builder.toString();
}
private static String prettyPrint(ClientEntity entity) {
return prettyPrint(entity.getProperties(), 0);
}
@SuppressWarnings("unchecked")
private static String prettyPrint(Map<String, Object> properties, int level) {
StringBuilder b = new StringBuilder();
Set<Entry<String, Object>> entries = properties.entrySet();
for (Entry<String, Object> entry : entries) {
intend(b, level);
b.append(entry.getKey()).append(": ");
Object value = entry.getValue();
if (value instanceof Map) {
value = prettyPrint((Map<String, Object>)value, level + 1);
} else if (value instanceof Calendar) {
Calendar cal = (Calendar)value;
value = DateFormat.getInstance().format(cal.getTime());
}
b.append(value).append("\n");
}
// remove last line break
b.deleteCharAt(b.length() - 1);
return b.toString();
}
private static String prettyPrint(Collection<ClientProperty> properties, int level) {
StringBuilder b = new StringBuilder();
for (Object property : properties) {
intend(b, level);
if (property instanceof ClientProperty) {
ClientProperty entry = (ClientProperty)property;
ClientValue value = entry.getValue();
if (value.isCollection()) {
ClientCollectionValue cclvalue = value.asCollection();
b.append(entry.getName()).append(": ");
b.append(prettyPrint(cclvalue.asJavaCollection(), level + 1));
} else if (value.isComplex()) {
ClientComplexValue cpxvalue = value.asComplex();
b.append(prettyPrint(cpxvalue.asJavaMap(), level + 1));
} else if (value.isEnum()) {
ClientEnumValue cnmvalue = value.asEnum();
b.append(entry.getName()).append(": ");
b.append(cnmvalue.getValue()).append("\n");
} else if (value.isPrimitive()) {
b.append(entry.getName()).append(": ");
b.append(entry.getValue()).append("\n");
}
} else {
b.append(property.toString()).append("\n");
}
}
return b.toString();
}
private static void intend(StringBuilder builder, int intendLevel) {
for (int i = 0; i < intendLevel; i++) {
builder.append(" ");
}
}
private static final class TestOlingo4ResponseHandler<T> implements Olingo4ResponseHandler<T> {
private T response;
private Exception error;
private CountDownLatch latch = new CountDownLatch(1);
@Override
public void onResponse(T response, Map<String, String> responseHeaders) {
this.response = response;
if (LOG.isDebugEnabled()) {
if (response instanceof ClientEntitySet) {
LOG.debug("Received response: {}", prettyPrint((ClientEntitySet)response));
} else if (response instanceof ClientEntity) {
LOG.debug("Received response: {}", prettyPrint((ClientEntity)response));
} else {
LOG.debug("Received response: {}", response);
}
}
latch.countDown();
}
@Override
public void onException(Exception ex) {
error = ex;
latch.countDown();
}
@Override
public void onCanceled() {
error = new IllegalStateException("Request Canceled");
latch.countDown();
}
public T await() throws Exception {
return await(TIMEOUT, TimeUnit.SECONDS);
}
public T await(long timeout, TimeUnit unit) throws Exception {
assertTrue("Timeout waiting for response", latch.await(timeout, unit));
if (error != null) {
throw error;
}
assertNotNull("Response", response);
return response;
}
public void reset() {
latch.countDown();
latch = new CountDownLatch(1);
response = null;
error = null;
}
}
}