package com.temenos.interaction.commands.odata;
/*
* #%L
* interaction-commands-odata
* %%
* Copyright (C) 2012 - 2013 Temenos Holdings N.V.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.odata4j.core.ImmutableList;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityKey;
import org.odata4j.edm.EdmDataServices;
import org.odata4j.edm.EdmEntitySet;
import org.odata4j.edm.EdmEntityType;
import org.odata4j.edm.EdmProperty;
import org.odata4j.edm.EdmSchema;
import org.odata4j.edm.EdmType;
import org.odata4j.expression.AndExpression;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.expression.EntitySimpleProperty;
import org.odata4j.expression.EqExpression;
import org.odata4j.expression.StringLiteral;
import org.odata4j.producer.EntitiesResponse;
import org.odata4j.producer.EntityQueryInfo;
import org.odata4j.producer.EntityResponse;
import org.odata4j.producer.ODataProducer;
import org.odata4j.producer.QueryInfo;
import com.temenos.interaction.core.MultivaluedMapImpl;
import com.temenos.interaction.core.command.InteractionContext;
import com.temenos.interaction.core.command.InteractionException;
import com.temenos.interaction.core.command.InteractionProducerException;
import com.temenos.interaction.core.entity.Metadata;
import com.temenos.interaction.core.hypermedia.Action;
import com.temenos.interaction.core.hypermedia.ResourceState;
import com.temenos.interaction.core.resource.EntityResource;
public class TestGETEntitiesCommand {
@Test
public void testExecuteForQueryOptionParsingErrors() {
// invalid value for $top
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$top", "foo");
InteractionContext mockContext = createInteractionContext("MyEntity", queryParams);
ODataProducer mockProducer = createMockODataProducer("MyEntity", "Edm.String");
GETEntitiesCommand command = new GETEntitiesCommand(mockProducer);
try {
command.execute(mockContext);
fail("InteractionException must be thrown");
} catch (InteractionException e) {
assertEquals(Status.BAD_REQUEST,e.getHttpStatus());
}
// invalid value for $skip
queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$skip", "foo");
mockContext = createInteractionContext("MyEntity", queryParams);
command = new GETEntitiesCommand(mockProducer);
try {
command.execute(mockContext);
fail("InteractionException must be thrown");
} catch (InteractionException e) {
assertEquals(Status.BAD_REQUEST,e.getHttpStatus());
}
}
@Test
public void testExecuteWithQueryParams() throws InteractionException {
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$filter", "a eq 'b'");
queryParams.add("$select", "a");
queryParams.add("MYPARAM", "MYVALUE");
InteractionContext mockContext = createInteractionContext("MyEntity", queryParams);
ODataProducer mockProducer = createMockODataProducer("MyEntity", "Edm.String");
GETEntitiesCommand command = new GETEntitiesCommand(mockProducer);
command.execute(mockContext);
verify(mockProducer).getEntities(any(String.class), argThat(new ArgumentMatcher<QueryInfo>() {
@Override
public boolean matches(Object argument) {
boolean result = false;
if(argument instanceof QueryInfo) {
QueryInfo queryInfo = (QueryInfo)argument;
Map<String,String> customOptions = queryInfo.customOptions;
if("a eq 'b'".equals(customOptions.get("$filter"))) {
if("a".equals(customOptions.get("$select"))) {
if("MYVALUE".equals(customOptions.get("MYPARAM"))) {
result = true;
}
}
}
}
return result;
}
}));
}
@Test
public void testExecuteQueryNoMandatoryInputs() {
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
InteractionContext mockContext = createInteractionContext("MyEntity", queryParams);
ODataProducer mockProducer = createMockODataProducerException("MyEntity", "Edm.String");
GETEntitiesCommand command = new GETEntitiesCommand(mockProducer);
try {
command.execute(mockContext);
fail("InteractionException must be thrown");
} catch (InteractionException e) {
assertEquals(Status.BAD_REQUEST, e.getHttpStatus());
assertNotNull(mockContext.getResource());
assertEquals(mockContext.getResource().getEntityName(), "Errors");
}
}
@Test
public void testExecuteWithQueryOptionFilterAndActionFilter()
throws Exception {
ODataProducer mockProducer = createMockODataProducer("MyEntity",
"Edm.String");
Properties actionFilter = new Properties();
actionFilter.put("filter", "foo1 eq 'bar1'");
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$filter", "foo2 eq 'bar2'");
executeWithFilters(mockProducer,
actionFilter, queryParams);
verify(mockProducer).getEntities(any(String.class),
argThat(new ArgumentMatcher<QueryInfo>() {
@Override
public boolean matches(Object argument) {
QueryInfo queryInfo = (QueryInfo) argument;
BoolCommonExpression filter = queryInfo.filter;
verifyAndExpression((AndExpression) filter,
new String[] { "foo1", "foo2" }, new String[] {
"bar1", "bar2" });
return true;
}
}));
}
@Test
public void testExecuteWithActionFilterOnly() throws Exception {
ODataProducer mockProducer = createMockODataProducer("MyEntity",
"Edm.String");
Properties actionFilter = new Properties();
actionFilter.put("filter", "foo eq 'bar'");
executeWithFilters(mockProducer,
actionFilter, new MultivaluedMapImpl<String>());
verify(mockProducer).getEntities(any(String.class),
argThat(new ArgumentMatcher<QueryInfo>() {
@Override
public boolean matches(Object argument) {
QueryInfo queryInfo = (QueryInfo) argument;
BoolCommonExpression filter = queryInfo.filter;
verifyEqExpression((EqExpression) filter, "foo",
"bar");
return true;
}
}));
}
@Test
public void testExecuteWithQueryOptionFilterOnly() throws Exception {
ODataProducer mockProducer = createMockODataProducer("MyEntity",
"Edm.String");
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$filter", "foo eq 'bar'");
executeWithFilters(mockProducer,
new Properties(), queryParams);
verify(mockProducer).getEntities(any(String.class),
argThat(new ArgumentMatcher<QueryInfo>() {
@Override
public boolean matches(Object argument) {
QueryInfo queryInfo = (QueryInfo) argument;
BoolCommonExpression filter = queryInfo.filter;
verifyEqExpression((EqExpression) filter, "foo",
"bar");
return true;
}
}));
}
// executes on supplied producer with query option and action filters
private void executeWithFilters(
ODataProducer mockProducer, Properties actionFilter,
MultivaluedMap<String, String> queryParams)
throws InteractionException {
Action mockAction = mock(Action.class);
when(mockAction.getProperties()).thenReturn(actionFilter);
ResourceState mockResourceState = mock(ResourceState.class);
when(mockResourceState.getViewAction()).thenReturn(mockAction);
when(mockResourceState.getEntityName()).thenReturn("MyEntity");
InteractionContext mockContext = mock(InteractionContext.class);
when(mockContext.getCurrentState()).thenReturn(mockResourceState);
when(mockContext.getQueryParameters()).thenReturn(queryParams);
GETEntitiesCommand command = new GETEntitiesCommand(mockProducer);
command.execute(mockContext);
}
// only supports 'eq' on both lhs and rhs
private void verifyAndExpression(AndExpression andExpr,
String[] expectedPropNames, String[] expectedPropValues) {
verifyEqExpression((EqExpression) andExpr.getLHS(),
expectedPropNames[0], expectedPropValues[0]);
verifyEqExpression((EqExpression) andExpr.getRHS(),
expectedPropNames[1], expectedPropValues[1]);
}
// only supports EntitySimpleProperty at lhs and StringLiteral at rhs
private void verifyEqExpression(EqExpression eqExpr, String expectedLhs,
String expectedRhs) {
assertEquals(expectedLhs,
((EntitySimpleProperty) eqExpr.getLHS()).getPropertyName());
assertEquals(expectedRhs, ((StringLiteral) eqExpr.getRHS()).getValue());
}
@Test
public void testExecuteWithInvalidInlineCount() {
try {
verifyForInlineCountValue("");
fail("InteractionException is expected");
} catch (Exception e) {
assertTrue(e instanceof InteractionException);
assertEquals(400, ((InteractionException) e).getHttpStatus()
.getStatusCode());
}
try {
verifyForInlineCountValue("foo");
fail("InteractionException is expected");
} catch (Exception e) {
assertTrue(e instanceof InteractionException);
assertEquals(400, ((InteractionException) e).getHttpStatus()
.getStatusCode());
}
}
@Test
public void testExecuteWithValidInlineCount() throws InteractionException {
verifyForInlineCountValue("allpages");
verifyForInlineCountValue("none");
}
private void verifyForInlineCountValue(final String inlineCount) throws InteractionException {
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl<String>();
queryParams.add("$inlinecount", inlineCount);
InteractionContext mockContext = createInteractionContext("MyEntity", queryParams);
ODataProducer mockProducer = createMockODataProducer("MyEntity", "Edm.String");
GETEntitiesCommand command = new GETEntitiesCommand(mockProducer);
command.execute(mockContext);
verify(mockProducer).getEntities(any(String.class), argThat(new ArgumentMatcher<QueryInfo>() {
@Override
public boolean matches(Object argument) {
if(argument instanceof QueryInfo) {
QueryInfo queryInfo = (QueryInfo)argument;
Map<String,String> customOptions = queryInfo.customOptions;
if(inlineCount.equals(customOptions.get("$inlinecount"))) {
return true;
}
}
return false;
}
}));
}
private ODataProducer createMockODataProducerException(String entityName, String keyTypeName) {
ODataProducer mockProducer = mock(ODataProducer.class);
List<String> keys = new ArrayList<String>();
keys.add("MyId");
List<EdmProperty.Builder> properties = new ArrayList<EdmProperty.Builder>();
EdmProperty.Builder ep = EdmProperty.newBuilder("MyId").setType(new MyEdmType(keyTypeName));
properties.add(ep);
EdmEntityType.Builder eet = EdmEntityType.newBuilder().setNamespace("MyNamespace").setAlias("MyAlias").setName(entityName).addKeys(keys).addProperties(properties);
EdmEntitySet.Builder ees = EdmEntitySet.newBuilder().setName(entityName).setEntityType(eet);
EdmSchema.Builder es = EdmSchema.newBuilder().setNamespace("MyNamespace");
List<EdmEntityType> mockEntityTypes = new ArrayList<EdmEntityType>();
mockEntityTypes.add(eet.build());
List<EdmSchema> mockSchemas = new ArrayList<EdmSchema>();
mockSchemas.add(es.build());
ImmutableList<EdmSchema> mockSchemaList = ImmutableList.copyOf(mockSchemas);
EdmDataServices mockEDS = mock(EdmDataServices.class);
when(mockEDS.getEdmEntitySet((EdmEntityType) any())).thenReturn(ees.build());
when(mockEDS.getEntityTypes()).thenReturn(mockEntityTypes);
when(mockEDS.findEdmEntityType(anyString())).thenReturn(eet.build());
when(mockEDS.getSchemas()).thenReturn(mockSchemaList);
when(mockProducer.getMetadata()).thenReturn(mockEDS);
EntityResponse mockEntityResponse = mock(EntityResponse.class);
OEntity oe = mock(OEntity.class);
when(oe.getEntityType()).thenReturn(eet.build());
when(oe.getEntitySetName()).thenReturn(ees.build().getName());
when(mockEntityResponse.getEntity()).thenReturn(oe);
when(mockProducer.getEntity(anyString(), any(OEntityKey.class), any(EntityQueryInfo.class))).thenReturn(mockEntityResponse);
InteractionProducerException mockErrorProducer = mock(InteractionProducerException.class);
EntityResource<?> entityResource1 = mock(EntityResource.class);
doReturn(entityResource1).when(mockErrorProducer).getEntityResource();
doReturn("Errors").when(entityResource1).getEntityName();
when(mockErrorProducer.getHttpStatus()).thenReturn(Status.BAD_REQUEST);
when(mockProducer.getEntities(any(String.class), any(QueryInfo.class))).thenThrow(mockErrorProducer);
return mockProducer;
}
private ODataProducer createMockODataProducer(String entityName, String keyTypeName) {
ODataProducer mockProducer = mock(ODataProducer.class);
List<String> keys = new ArrayList<String>();
keys.add("MyId");
List<EdmProperty.Builder> properties = new ArrayList<EdmProperty.Builder>();
EdmProperty.Builder ep = EdmProperty.newBuilder("MyId").setType(new MyEdmType(keyTypeName));
properties.add(ep);
EdmEntityType.Builder eet = EdmEntityType.newBuilder().setNamespace("MyNamespace").setAlias("MyAlias").setName(entityName).addKeys(keys).addProperties(properties);
EdmEntitySet.Builder ees = EdmEntitySet.newBuilder().setName(entityName).setEntityType(eet);
EdmSchema.Builder es = EdmSchema.newBuilder().setNamespace("MyNamespace");
List<EdmEntityType> mockEntityTypes = new ArrayList<EdmEntityType>();
mockEntityTypes.add(eet.build());
List<EdmSchema> mockSchemas = new ArrayList<EdmSchema>();
mockSchemas.add(es.build());
ImmutableList<EdmSchema> mockSchemaList = ImmutableList.copyOf(mockSchemas);
EdmDataServices mockEDS = mock(EdmDataServices.class);
when(mockEDS.getEdmEntitySet((EdmEntityType) any())).thenReturn(ees.build());
when(mockEDS.getEntityTypes()).thenReturn(mockEntityTypes);
when(mockEDS.findEdmEntityType(anyString())).thenReturn(eet.build());
when(mockEDS.getSchemas()).thenReturn(mockSchemaList);
when(mockProducer.getMetadata()).thenReturn(mockEDS);
EntityResponse mockEntityResponse = mock(EntityResponse.class);
OEntity oe = mock(OEntity.class);
when(oe.getEntityType()).thenReturn(eet.build());
when(oe.getEntitySetName()).thenReturn(ees.build().getName());
when(mockEntityResponse.getEntity()).thenReturn(oe);
when(mockProducer.getEntity(anyString(), any(OEntityKey.class), any(EntityQueryInfo.class))).thenReturn(mockEntityResponse);
EntitiesResponse mockEntityResponse2 = mock(EntitiesResponse.class);
when(mockProducer.getEntities(any(String.class), any(QueryInfo.class))).thenReturn(mockEntityResponse2);
return mockProducer;
}
@SuppressWarnings("unchecked")
private InteractionContext createInteractionContext(String entity, MultivaluedMap<String, String> queryParams) {
ResourceState resourceState = mock(ResourceState.class);
when(resourceState.getEntityName()).thenReturn(entity);
InteractionContext ctx = new InteractionContext(mock(UriInfo.class), mock(HttpHeaders.class), mock(MultivaluedMap.class), queryParams ,resourceState, mock(Metadata.class));
return ctx;
}
class MyEdmType extends EdmType {
public MyEdmType(String name) {
super(name);
}
public boolean isSimple() { return false; }
}
}