diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java index 0e4fe1702..49624ab27 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/PrimitiveComplexITCase.java @@ -22,6 +22,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.net.URI; @@ -112,6 +113,27 @@ public class PrimitiveComplexITCase extends AbstractBaseTestITCase { } } + @Test + public void deletePrimitiveCollection() throws Exception { + final URI uri = getClient().newURIBuilder(SERVICE_URI) + .appendEntitySetSegment("ESMixPrimCollComp").appendKeySegment(7).appendPropertySegment("CollPropertyString") + .build(); + final ODataDeleteResponse response = getClient().getCUDRequestFactory().getDeleteRequest(uri).execute(); + assertEquals(HttpStatusCode.NO_CONTENT.getStatusCode(), response.getStatusCode()); + + // Check that the property is not gone but empty now. + // This check has to be in the same session in order to access the same data provider. + ODataPropertyRequest request = getClient().getRetrieveRequestFactory() + .getPropertyRequest(uri); + request.addCustomHeader(HttpHeader.COOKIE, response.getHeader(HttpHeader.SET_COOKIE).iterator().next()); + final ODataRetrieveResponse propertyResponse = request.execute(); + assertEquals(HttpStatusCode.OK.getStatusCode(), propertyResponse.getStatusCode()); + final ODataProperty property = propertyResponse.getBody(); + assertNotNull(property); + assertNotNull(property.getCollectionValue()); + assertTrue(property.getCollectionValue().isEmpty()); + } + @Test public void readComplexProperty() throws Exception { ODataPropertyRequest request = getClient().getRetrieveRequestFactory() diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java index b66856dfd..5db1d112c 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java @@ -43,6 +43,7 @@ import org.apache.olingo.server.api.uri.UriResourceFunction; import org.apache.olingo.server.api.uri.UriResourceKind; import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourcePartTyped; +import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.api.uri.UriResourceSingleton; import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption; import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind; @@ -175,6 +176,7 @@ public class UriValidator { validateForHttpMethod(uriInfo, httpMethod); validateQueryOptions(uriInfo); validateKeyPredicates(uriInfo); + validatePropertyOperations(uriInfo, httpMethod); } private ColumnIndex colIndex(final SystemQueryOptionKind queryOptionKind) throws UriValidationException { @@ -667,4 +669,26 @@ public class UriValidator { } } } + + private void validatePropertyOperations(final UriInfo uriInfo, final HttpMethod method) + throws UriValidationException { + final List parts = uriInfo.getUriResourceParts(); + final UriResource last = parts.size() > 0 ? parts.get(parts.size() - 1) : null; + final UriResource previous = parts.size() > 1 ? parts.get(parts.size() - 2) : null; + if (last != null + && (last.getKind() == UriResourceKind.primitiveProperty + || last.getKind() == UriResourceKind.complexProperty + || last.getKind() == UriResourceKind.value && previous.getKind() == UriResourceKind.primitiveProperty)) { + final EdmProperty property = ((UriResourceProperty) + (last.getKind() == UriResourceKind.value ? previous : last)).getProperty(); + if (method == HttpMethod.PATCH && property.isCollection()) { + throw new UriValidationException("Attempt to patch collection property.", + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, method.toString()); + } + if (method == HttpMethod.DELETE && property.isNullable() != null && !property.isNullable()) { + throw new UriValidationException("Attempt to delete non-nullable property.", + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, method.toString()); + } + } + } } diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java index 1cb76915f..d4889d766 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java @@ -18,6 +18,7 @@ */ package org.apache.olingo.server.tecsvc.processor; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -84,7 +85,8 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor public void updatePrimitive(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, final ContentType requestFormat, final ContentType responseFormat) throws ODataApplicationException, DeserializerException, SerializerException { - throw new UnsupportedOperationException("Actual not yet supported"); + throw new ODataApplicationException("Not supported yet.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } @Override @@ -103,7 +105,8 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor public void updatePrimitiveCollection(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, final ContentType requestFormat, final ContentType responseFormat) throws ODataApplicationException, DeserializerException, SerializerException { - throw new UnsupportedOperationException("Actual not yet supported"); + throw new ODataApplicationException("Not supported yet.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } @Override @@ -122,7 +125,8 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor public void updateComplex(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, final ContentType requestFormat, final ContentType responseFormat) throws ODataApplicationException, DeserializerException, SerializerException { - throw new UnsupportedOperationException("Actual not yet supported"); + throw new ODataApplicationException("Not supported yet.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } @Override @@ -141,7 +145,8 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor public void updateComplexCollection(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo, final ContentType requestFormat, final ContentType responseFormat) throws ODataApplicationException, DeserializerException, SerializerException { - throw new UnsupportedOperationException("Actual not yet supported"); + throw new ODataApplicationException("Not supported yet.", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } @Override @@ -226,7 +231,7 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor final Property property = getPropertyData(resourceEntitySet, path); if (edmProperty.isNullable() == null || edmProperty.isNullable()) { - property.setValue(property.getValueType(), null); + property.setValue(property.getValueType(), edmProperty.isCollection() ? Collections.emptyList() : null); response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); } else { throw new ODataApplicationException("Not nullable.", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java index a611c846f..564d1ec37 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java @@ -310,7 +310,7 @@ public class ODataHandlerTest { @Test public void dispatchPrimitiveProperty() throws Exception { - final String uri = "ESAllPrim(0)/PropertyInt16"; + final String uri = "ESAllPrim(0)/PropertyString"; final PrimitiveProcessor processor = mock(PrimitiveProcessor.class); dispatch(HttpMethod.GET, uri, processor); @@ -325,7 +325,7 @@ public class ODataHandlerTest { @Test public void dispatchPrimitivePropertyValue() throws Exception { - final String uri = "ESAllPrim(0)/PropertyInt16/$value"; + final String uri = "ESAllPrim(0)/PropertyString/$value"; final PrimitiveValueProcessor processor = mock(PrimitiveValueProcessor.class); dispatch(HttpMethod.GET, uri, processor); diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java index 7a5c692ce..9ad799af7 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/validator/UriValidatorTest.java @@ -346,20 +346,23 @@ public class UriValidatorTest { public void checkNonValidSystemQueryOption() throws Exception { for (final String[] uriArray : urisWithNonValidSystemQueryOptions) { final String[] uri = constructUri(uriArray); - try { - new UriValidator().validate( - new Parser().parseUri(uri[0], uri[1], null, edm), - HttpMethod.GET); - fail("Validation Exception not thrown: " + uri[0] + '?' + uri[1]); - } catch (final UriParserException e) { - fail("Wrong Exception thrown: " + uri[0] + '?' + uri[1]); - } catch (final UriValidationException e) { - assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, - e.getMessageKey()); - } + validateWrong(uri[0], uri[1], HttpMethod.GET, + UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED); } } + @Test + public void propertyOperations() throws Exception { + validateWrong(URI_PROPERTY_PRIMITIVE_COLLECTION, null, HttpMethod.PATCH, + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD); + validateWrong(URI_PROPERTY_COMPLEX_COLLECTION, null, HttpMethod.PATCH, + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD); + validateWrong("ESKeyNav(1)/PropertyString", null, HttpMethod.DELETE, + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD); + validateWrong("ESKeyNav(1)/PropertyString/$value", null, HttpMethod.DELETE, + UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD); + } + private String[] constructUri(final String[] uriParameterArray) { final String path = uriParameterArray[0]; String query = ""; @@ -371,4 +374,16 @@ public class UriValidatorTest { } return new String[] { path, query }; } + + private void validateWrong(final String path, final String query, final HttpMethod method, + final UriValidationException.MessageKeys expectedMessageKey) { + try { + new UriValidator().validate(new Parser().parseUri(path, query, null, edm), method); + fail("Validation Exception not thrown: " + method + ' ' + path + '?' + query); + } catch (final UriParserException e) { + fail("Wrong Exception thrown: " + method + ' ' + path + '?' + query); + } catch (final UriValidationException e) { + assertEquals(expectedMessageKey, e.getMessageKey()); + } + } }