blob: 0aa24dec434f1e6a0bedf758fe05e0735f764656 [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.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.camel.CamelExecutionException;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
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.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.ClientPrimitiveValue;
import org.apache.olingo.client.api.domain.ClientServiceDocument;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.ex.ODataError;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test class for {@link org.apache.camel.component.olingo4.api.Olingo4App}
* APIs.
* <p>
* The integration test runs against using the sample OData 4.0 remote TripPin
* service published on http://services.odata.org/TripPinRESTierService.
* </p>
*/
public class Olingo4ComponentProducerTest extends AbstractOlingo4TestSupport {
private static final Logger LOG = LoggerFactory.getLogger(Olingo4ComponentProducerTest.class);
private static final String PEOPLE = "People";
private static final String TEST_PEOPLE = "People('russellwhyte')";
private static final String TEST_CREATE_KEY = "'lewisblack'";
private static final String TEST_CREATE_PEOPLE = PEOPLE + "(" + TEST_CREATE_KEY + ")";
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_JSON = "{\n" + " \"UserName\": \"lewisblack\",\n" + " \"FirstName\": \"Lewis\",\n" + " \"LastName\": \"Black\"\n" + "}";
private static final String TEST_UPDATE_JSON = "{\n" + " \"UserName\": \"lewisblack\",\n" + " \"FirstName\": \"Lewis\",\n" + " \"MiddleName\": \"Black\",\n"
+ " \"LastName\": \"Black\"\n" + "}";
@Test
public void testRead() throws Exception {
final Map<String, Object> headers = new HashMap<>();
// Read metadata ($metadata) object
final Edm metadata = (Edm)requestBodyAndHeaders("direct:readmetadata", null, headers);
assertNotNull(metadata);
assertEquals(1, metadata.getSchemas().size());
// Read service document object
final ClientServiceDocument document = (ClientServiceDocument)requestBodyAndHeaders("direct:readdocument", null, headers);
assertNotNull(document);
assertTrue(document.getEntitySets().size() > 1);
LOG.info("Service document has {} entity sets", document.getEntitySets().size());
// Read entity set of the People object
final ClientEntitySet entities = (ClientEntitySet)requestBodyAndHeaders("direct:readentities", null, headers);
assertNotNull(entities);
assertEquals(5, entities.getEntities().size());
// Read object count with query options passed through header
final Long count = (Long)requestBodyAndHeaders("direct:readcount", null, headers);
assertEquals(20, count.intValue());
final ClientPrimitiveValue value = (ClientPrimitiveValue)requestBodyAndHeaders("direct:readvalue", null, headers);
LOG.info("Client value \"{}\" has type {}", value, value.getTypeName());
assertEquals("Male", value.asPrimitive().toString());
final ClientPrimitiveValue singleProperty = (ClientPrimitiveValue)requestBodyAndHeaders("direct:readsingleprop", null, headers);
assertTrue(singleProperty.isPrimitive());
assertEquals("San Francisco International Airport", singleProperty.toString());
final ClientComplexValue complexProperty = (ClientComplexValue)requestBodyAndHeaders("direct:readcomplexprop", null, headers);
assertTrue(complexProperty.isComplex());
assertEquals("San Francisco", complexProperty.get("City").getComplexValue().get("Name").getValue().toString());
final ClientCollectionValue<?> collectionProperty = (ClientCollectionValue<?>)requestBodyAndHeaders("direct:readcollectionprop", null, headers);
assertTrue(collectionProperty.isCollection());
assertEquals(1, collectionProperty.size());
Iterator<?> propIter = collectionProperty.iterator();
Object propValueObj = propIter.next();
assertIsInstanceOf(ClientComplexValue.class, propValueObj);
ClientComplexValue propValue = (ClientComplexValue)propValueObj;
assertEquals("Boise", propValue.get("City").getComplexValue().get("Name").getValue().toString());
final ClientEntity entity = (ClientEntity)requestBodyAndHeaders("direct:readentitybyid", null, headers);
assertNotNull(entity);
assertEquals("Russell", entity.getProperty("FirstName").getValue().toString());
final ClientEntity unbFuncReturn = (ClientEntity)requestBodyAndHeaders("direct:callunboundfunction", null, headers);
assertNotNull(unbFuncReturn);
}
@Test
public void testReadWithFilter() {
// Read entity set with filter of the Airports object
final ClientEntitySet entities = (ClientEntitySet)requestBody("direct:readwithfilter", null);
assertNotNull(entities);
assertEquals(1, entities.getEntities().size());
}
@Test
public void testCreateUpdateDelete() throws Exception {
final ClientEntity clientEntity = createEntity();
ClientEntity entity = requestBody("direct:create-entity", clientEntity);
assertNotNull(entity);
assertEquals("Lewis", entity.getProperty("FirstName").getValue().toString());
assertEquals("", entity.getProperty("MiddleName").getValue().toString());
// update
clientEntity.getProperties().add(objFactory.newPrimitiveProperty("MiddleName", objFactory.newPrimitiveValueBuilder().buildString("Lewis")));
HttpStatusCode status = requestBody("direct:update-entity", clientEntity);
assertNotNull("Update status", status);
assertEquals("Update status", HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
LOG.info("Update entity status: {}", status);
// delete
status = requestBody("direct:delete-entity", null);
assertNotNull("Delete status", status);
assertEquals("Delete status", HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
LOG.info("Delete status: {}", status);
// check for delete
try {
requestBody("direct:read-deleted-entity", null);
} catch (CamelExecutionException e) {
String causeMsg = e.getCause().getMessage();
assertTrue(causeMsg.contains("[HTTP/1.1 404 Not Found]"));
}
}
@Test
public void testCreateUpdateDeleteFromJson() throws Exception {
ClientEntity entity = requestBody("direct:create-entity", TEST_CREATE_JSON);
assertNotNull(entity);
assertEquals("Lewis", entity.getProperty("FirstName").getValue().toString());
assertEquals("Black", entity.getProperty("LastName").getValue().toString());
assertEquals("lewisblack", entity.getProperty("UserName").getValue().toString());
assertEquals("", entity.getProperty("MiddleName").getValue().toString());
// update
HttpStatusCode status = requestBody("direct:update-entity", TEST_UPDATE_JSON);
assertNotNull("Update status", status);
assertEquals("Update status", HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
LOG.info("Update entity status: {}", status);
// delete
status = requestBody("direct:delete-entity", null);
assertNotNull("Delete status", status);
assertEquals("Delete status", HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
LOG.info("Delete status: {}", status);
// check for delete
try {
requestBody("direct:read-deleted-entity", null);
} catch (CamelExecutionException e) {
String causeMsg = e.getCause().getMessage();
assertTrue(causeMsg.contains("[HTTP/1.1 404 Not Found]"));
}
}
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;
}
@Test
public void testBatch() throws Exception {
final List<Olingo4BatchRequest> batchParts = new ArrayList<>();
// 1. Edm query
batchParts.add(Olingo4BatchQueryRequest.resourcePath(Constants.METADATA).resourceUri(TEST_SERVICE_BASE_URL).build());
// 2. Read entities
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 $top
final HashMap<String, String> queryParams = new HashMap<>();
queryParams.put(SystemQueryOptionKind.TOP.toString(), "5");
batchParts.add(Olingo4BatchQueryRequest.resourcePath(PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).queryParams(queryParams).build());
// 5. Create entity
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 middle name in created entry
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 deleted entity to verify delete
batchParts.add(Olingo4BatchQueryRequest.resourcePath(TEST_CREATE_PEOPLE).resourceUri(TEST_SERVICE_BASE_URL).build());
// execute batch request
final List<Olingo4BatchResponse> responseParts = requestBody("direct:batch", batchParts);
assertNotNull("Batch response", responseParts);
assertEquals("Batch responses expected", 8, responseParts.size());
final Edm edm = (Edm)responseParts.get(0).getBody();
assertNotNull(edm);
LOG.info("Edm entity sets: {}", edm.getEntityContainer().getEntitySets());
ClientEntitySet entitySet = (ClientEntitySet)responseParts.get(1).getBody();
assertNotNull(entitySet);
LOG.info("Read entities: {}", entitySet.getEntities());
clientEntity = (ClientEntity)responseParts.get(2).getBody();
assertNotNull(clientEntity);
LOG.info("Read entiry properties: {}", clientEntity.getProperties());
ClientEntitySet entitySetWithTop = (ClientEntitySet)responseParts.get(3).getBody();
assertNotNull(entitySetWithTop);
assertEquals(5, entitySetWithTop.getEntities().size());
LOG.info("Read entities with $top=5: {}", entitySet.getEntities());
clientEntity = (ClientEntity)responseParts.get(4).getBody();
assertNotNull(clientEntity);
LOG.info("Created entity: {}", clientEntity.getProperties());
int statusCode = responseParts.get(5).getStatusCode();
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), statusCode);
LOG.info("Update MdiddleName status: {}", statusCode);
statusCode = responseParts.get(6).getStatusCode();
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), statusCode);
LOG.info("Delete entity status: {}", statusCode);
assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), responseParts.get(7).getStatusCode());
final ODataError error = (ODataError)responseParts.get(7).getBody();
assertNotNull(error);
LOG.info("Read deleted entity error: {}", error.getMessage());
}
@Test
public void testUnboundActionRequest() throws Exception {
final HttpStatusCode status = requestBody("direct:unbound-action-ResetDataSource", null);
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
}
@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 HttpStatusCode status = requestBody("direct:bound-action-people", clientEntity);
assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), status.getStatusCode());
}
@SuppressWarnings("unchecked")
@Test
public void testEndpointHttpHeaders() throws Exception {
final Map<String, Object> headers = new HashMap<>();
final ClientEntity entity = (ClientEntity)requestBodyAndHeaders("direct:read-etag", null, headers);
MockEndpoint mockEndpoint = getMockEndpoint("mock:check-etag-header");
mockEndpoint.expectedMessageCount(1);
mockEndpoint.assertIsSatisfied();
Map<String, String> responseHttpHeaders = (Map<String, String>)mockEndpoint.getExchanges().get(0).getIn().getHeader("CamelOlingo4.responseHttpHeaders");
assertEquals(responseHttpHeaders.get("ETag"), entity.getETag());
Map<String, String> endpointHttpHeaders = new HashMap<>();
endpointHttpHeaders.put("If-Match", entity.getETag());
headers.put("CamelOlingo4.endpointHttpHeaders", endpointHttpHeaders);
requestBodyAndHeaders("direct:delete-with-etag", null, headers);
// check for deleted entity with ETag
try {
requestBody("direct:read-etag", null);
} catch (CamelExecutionException e) {
assertStringContains(e.getCause().getMessage(), "The request resource is not found.");
}
}
/**
* Read entity set of the People object and with no filter already seen, all
* items should be present in each message
*
* @throws Exception
*/
@Test
public void testProducerReadNoFilterAlreadySeen() throws Exception {
final Map<String, Object> headers = new HashMap<>();
String endpoint = "direct:read-people-nofilterseen";
int expectedEntities = 20;
int expectedMsgCount = 3;
MockEndpoint mockEndpoint = getMockEndpoint("mock:producer-noalreadyseen");
mockEndpoint.expectedMessageCount(expectedMsgCount);
for (int i = 0; i < expectedMsgCount; ++i) {
final ClientEntitySet entities = (ClientEntitySet)requestBodyAndHeaders(endpoint, null, headers);
assertNotNull(entities);
}
mockEndpoint.assertIsSatisfied();
for (int i = 0; i < expectedMsgCount; ++i) {
Object body = mockEndpoint.getExchanges().get(i).getIn().getBody();
assertTrue(body instanceof ClientEntitySet);
ClientEntitySet set = (ClientEntitySet)body;
//
// All messages contained all the entities
//
assertEquals(expectedEntities, set.getEntities().size());
}
}
/**
* Read entity set of the People object and filter already seen items on
* subsequent exchanges
*/
@Test
public void testProducerReadFilterAlreadySeen() throws Exception {
final Map<String, Object> headers = new HashMap<>();
String endpoint = "direct:read-people-filterseen";
int expectedEntities = 20;
int expectedMsgCount = 3;
MockEndpoint mockEndpoint = getMockEndpoint("mock:producer-alreadyseen");
mockEndpoint.expectedMessageCount(expectedMsgCount);
for (int i = 0; i < expectedMsgCount; ++i) {
final ClientEntitySet entities = (ClientEntitySet)requestBodyAndHeaders(endpoint, null, headers);
assertNotNull(entities);
}
mockEndpoint.assertIsSatisfied();
for (int i = 0; i < expectedMsgCount; ++i) {
Object body = mockEndpoint.getExchanges().get(i).getIn().getBody();
assertTrue(body instanceof ClientEntitySet);
ClientEntitySet set = (ClientEntitySet)body;
if (i == 0) {
//
// First polled messages contained all the entities
//
assertEquals(expectedEntities, set.getEntities().size());
} else {
//
// Subsequent messages should be empty
// since the filterAlreadySeen property is true
//
assertEquals(0, set.getEntities().size());
}
}
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
public void configure() {
// test routes for read
from("direct:readmetadata").to("olingo4://read/$metadata");
from("direct:readdocument").to("olingo4://read/");
from("direct:readentities").to("olingo4://read/" + PEOPLE + "?$top=5&$orderby=FirstName asc");
from("direct:readcount").to("olingo4://read/" + PEOPLE + "/$count");
from("direct:readvalue").to("olingo4://read/" + TEST_PEOPLE + "/Gender/$value");
from("direct:readsingleprop").to("olingo4://read/Airports('KSFO')/Name");
from("direct:readcomplexprop").to("olingo4://read/Airports('KSFO')/Location");
from("direct:readcollectionprop").to("olingo4://read/" + TEST_PEOPLE + "/AddressInfo");
from("direct:readentitybyid").to("olingo4://read/" + TEST_PEOPLE + "");
from("direct:readwithfilter").to("olingo4://read/Airports?$filter=Name eq 'San Francisco International Airport'");
from("direct:callunboundfunction").to("olingo4://read/GetNearestAirport(lat=33,lon=-118)");
// test route for create individual entity
from("direct:create-entity").to("olingo4://create/" + PEOPLE);
// test route for update
from("direct:update-entity").to("olingo4://update/" + PEOPLE + "('lewisblack')");
// test route for delete
from("direct:delete-entity").to("olingo4://delete/" + PEOPLE + "('lewisblack')");
// test route for delete
from("direct:read-deleted-entity").to("olingo4://delete/" + PEOPLE + "('lewisblack')");
// test route for batch
from("direct:batch").to("olingo4://batch");
from("direct:read-etag").to("olingo4://read/Airlines('AA')").to("mock:check-etag-header");
from("direct:delete-with-etag").to("olingo4://delete/Airlines('AA')");
from("direct:read-people-nofilterseen").to("olingo4://read/" + PEOPLE).to("mock:producer-noalreadyseen");
from("direct:read-people-filterseen").to("olingo4://read/" + PEOPLE + "?filterAlreadySeen=true").to("mock:producer-alreadyseen");
// test routes action's
from("direct:unbound-action-ResetDataSource").to("olingo4://action/ResetDataSource");
from("direct:bound-action-people").to("olingo4://action/" + TEST_PEOPLE + "/Microsoft.OData.Service.Sample.TrippinInMemory.Models.ShareTrip");
}
};
}
}