[OLINGO-653] Fix SystemQueryOptions on Post Issue
This commit is contained in:
parent
9729428c22
commit
0457e24fa7
|
@ -60,7 +60,7 @@ public class UriValidator {
|
|||
/* entitySetCount 7 */ { true , false, false, false, false, false, true, false, false, false, false },
|
||||
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false },
|
||||
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* references 10 */ { true , true , false, true, false, true , true , false, true , true , true },
|
||||
/* references 10 */ { true , true , false, true, true , true , true , false, true , true , true },
|
||||
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false },
|
||||
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true },
|
||||
|
@ -71,16 +71,6 @@ public class UriValidator {
|
|||
/* propertyPrimitiveValue 18 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* none 19 */ { false, true , false, false, false, false, false, false, false, false, false }
|
||||
};
|
||||
|
||||
private final boolean[][] decisionMatrixForHttpMethod =
|
||||
{
|
||||
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-TOP */
|
||||
/* GET 0 */ { true , true , true , true, true , true , true , true , true , true , true },
|
||||
/* POST 0 */ { true , false , true , false, false , true , false , true , false , false , false },
|
||||
/* PUT 0 */ { false , false , false , false, false , false , false , false , false , false , false },
|
||||
/* DELETE 0 */ { false , false , false , true, false , false, false , false, false , false , false },
|
||||
/* PATCH 0 */ { false , false , false , false, false , false , false , false , false , false , false }
|
||||
};
|
||||
//CHECKSTYLE:ON
|
||||
//@formatter:on
|
||||
|
||||
|
@ -141,30 +131,10 @@ public class UriValidator {
|
|||
}
|
||||
}
|
||||
|
||||
private enum RowIndexForHttpMethod {
|
||||
GET(0),
|
||||
POST(1),
|
||||
PUT(2),
|
||||
DELETE(3),
|
||||
PATCH(4);
|
||||
|
||||
private int idx;
|
||||
|
||||
RowIndexForHttpMethod(final int i) {
|
||||
idx = i;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
public UriValidator() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void validate(final UriInfo uriInfo, final HttpMethod httpMethod) throws UriValidationException {
|
||||
if (HttpMethod.GET != httpMethod) {
|
||||
validateForHttpMethod(uriInfo, httpMethod);
|
||||
}
|
||||
validateQueryOptions(uriInfo);
|
||||
validateKeyPredicates(uriInfo);
|
||||
validatePropertyOperations(uriInfo, httpMethod);
|
||||
|
@ -484,44 +454,70 @@ public class UriValidator {
|
|||
}
|
||||
|
||||
private void validateForHttpMethod(final UriInfo uriInfo, final HttpMethod httpMethod) throws UriValidationException {
|
||||
RowIndexForHttpMethod row = rowIndexForHttpMethod(httpMethod);
|
||||
|
||||
switch (httpMethod) {
|
||||
case POST:
|
||||
if (!isAction(uriInfo)) {
|
||||
// POST and SystemQueryOptions only allowed if addressed resource is an action
|
||||
validateNoQueryOptionsForHttpMethod(uriInfo, httpMethod);
|
||||
}
|
||||
break;
|
||||
case DELETE:
|
||||
if (!isReferences(uriInfo)) {
|
||||
// DELETE and SystemQueryOptions only allowed if addressed resource is a reference collection
|
||||
validateNoQueryOptionsForHttpMethod(uriInfo, httpMethod);
|
||||
} else {
|
||||
// Only $id allowed as SystemQueryOption for DELETE and references
|
||||
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
|
||||
ColumnIndex col = colIndex(option.getKind());
|
||||
if (!decisionMatrixForHttpMethod[row.getIndex()][col.getIndex()]) {
|
||||
if (SystemQueryOptionKind.ID != option.getKind()) {
|
||||
throw new UriValidationException("System query option " + option.getName() + " not allowed for method "
|
||||
+ httpMethod, UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD,
|
||||
option.getName(), httpMethod.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private RowIndexForHttpMethod rowIndexForHttpMethod(final HttpMethod httpMethod) throws UriValidationException {
|
||||
RowIndexForHttpMethod idx;
|
||||
|
||||
switch (httpMethod) {
|
||||
case GET:
|
||||
idx = RowIndexForHttpMethod.GET;
|
||||
break;
|
||||
case POST:
|
||||
idx = RowIndexForHttpMethod.POST;
|
||||
break;
|
||||
case PUT:
|
||||
idx = RowIndexForHttpMethod.PUT;
|
||||
break;
|
||||
case DELETE:
|
||||
idx = RowIndexForHttpMethod.DELETE;
|
||||
break;
|
||||
case PATCH:
|
||||
idx = RowIndexForHttpMethod.PATCH;
|
||||
// PUT and PATCH do not allow system query options
|
||||
validateNoQueryOptionsForHttpMethod(uriInfo, httpMethod);
|
||||
break;
|
||||
default:
|
||||
throw new UriValidationException("HTTP method not supported: " + httpMethod,
|
||||
UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, httpMethod.toString());
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
private boolean isReferences(final UriInfo uriInfo) {
|
||||
if (!uriInfo.getSystemQueryOptions().isEmpty()) {
|
||||
List<UriResource> uriResourceParts = uriInfo.getUriResourceParts();
|
||||
if (UriResourceKind.ref == uriResourceParts.get(uriResourceParts.size() - 1).getKind()) {
|
||||
UriResourcePartTyped previousSegment = (UriResourcePartTyped) uriResourceParts.get(uriResourceParts.size() - 2);
|
||||
return previousSegment.isCollection();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void validateNoQueryOptionsForHttpMethod(final UriInfo uriInfo, final HttpMethod httpMethod)
|
||||
throws UriValidationException {
|
||||
if (!uriInfo.getSystemQueryOptions().isEmpty()) {
|
||||
String options = "";
|
||||
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
|
||||
options = options + option.getName() + " ";
|
||||
}
|
||||
throw new UriValidationException("System query option " + options + " not allowed for method "
|
||||
+ httpMethod, UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD,
|
||||
options, httpMethod.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAction(final UriInfo uriInfo) {
|
||||
List<UriResource> uriResourceParts = uriInfo.getUriResourceParts();
|
||||
if (!uriResourceParts.isEmpty()) {
|
||||
return UriResourceKind.action == uriResourceParts.get(uriResourceParts.size() - 1).getKind();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void validateKeyPredicates(final UriInfo uriInfo) throws UriValidationException {
|
||||
|
|
|
@ -36,6 +36,13 @@ import org.junit.Test;
|
|||
|
||||
public class UriValidatorTest {
|
||||
|
||||
private static final String URI_ACTION_NO_RETURN = "AIRT";
|
||||
private static final String URI_ACTION_PRIM = "AIRTString";
|
||||
private static final String URI_ACTION_COOL_PRIM = "AIRTCollStringTwoParam";
|
||||
private static final String URI_ACTION_CT = "AIRTCTTwoPrimParam";
|
||||
private static final String URI_ACTION_COLL_CT = "AIRTCollCTTwoPrimParam";
|
||||
private static final String URI_ACTION_ENTITY = "AIRTESAllPrimParam";
|
||||
private static final String URI_ACTION_ES = "AIRTCollESAllPrimParam";
|
||||
private static final String URI_ALL = "$all";
|
||||
private static final String URI_BATCH = "$batch";
|
||||
private static final String URI_CROSSJOIN = "$crossjoin(ESAllPrim)";
|
||||
|
@ -105,7 +112,7 @@ public class UriValidatorTest {
|
|||
|
||||
{ URI_REFERENCES, QO_FILTER }, { URI_REFERENCES, QO_FORMAT }, { URI_REFERENCES, QO_ORDERBY },
|
||||
/* { URI_REFERENCES, QO_SEARCH }, */{ URI_REFERENCES, QO_SKIP }, { URI_REFERENCES, QO_SKIPTOKEN },
|
||||
{ URI_REFERENCES, QO_TOP }, { URI_REFERENCES, QO_ID },
|
||||
{ URI_REFERENCES, QO_TOP }, { URI_REFERENCES, QO_ID }, { URI_REFERENCES, QO_COUNT },
|
||||
|
||||
{ URI_REFERENCE, QO_FORMAT },
|
||||
|
||||
|
@ -199,8 +206,7 @@ public class UriValidatorTest {
|
|||
/* { URI_MEDIA_STREAM, QO_SEARCH }, */{ URI_MEDIA_STREAM, QO_SELECT }, { URI_MEDIA_STREAM, QO_SKIP },
|
||||
{ URI_MEDIA_STREAM, QO_SKIPTOKEN }, { URI_MEDIA_STREAM, QO_TOP },
|
||||
|
||||
{ URI_REFERENCES, QO_EXPAND }, { URI_REFERENCES, QO_COUNT },
|
||||
{ URI_REFERENCES, QO_SELECT },
|
||||
{ URI_REFERENCES, QO_EXPAND }, { URI_REFERENCES, QO_SELECT },
|
||||
|
||||
{ URI_REFERENCE, QO_FILTER }, { URI_REFERENCE, QO_ID }, { URI_REFERENCE, QO_EXPAND },
|
||||
{ URI_REFERENCE, QO_COUNT }, { URI_REFERENCE, QO_ORDERBY }, /* { URI_REFERENCE, QO_SEARCH }, */
|
||||
|
@ -263,8 +269,104 @@ public class UriValidatorTest {
|
|||
{ URI_FI_ENTITY_SET_KEY, QO_SKIP }, { URI_FI_ENTITY_SET_KEY, QO_SKIPTOKEN }, { URI_FI_ENTITY_SET_KEY, QO_TOP }
|
||||
};
|
||||
|
||||
private final String[][] actionWithValidSystemQueryOptions = {
|
||||
// NoReturnType
|
||||
{ URI_ACTION_NO_RETURN, QO_FORMAT },
|
||||
// PrimReturnType
|
||||
{ URI_ACTION_PRIM, QO_FORMAT },
|
||||
// PrimCollectionReturnType
|
||||
{ URI_ACTION_COOL_PRIM, QO_FILTER }, { URI_ACTION_COOL_PRIM, QO_FORMAT },
|
||||
{ URI_ACTION_COOL_PRIM, QO_COUNT }, { URI_ACTION_COOL_PRIM, QO_ORDERBY },
|
||||
{ URI_ACTION_COOL_PRIM, QO_SKIP }, { URI_ACTION_COOL_PRIM, QO_SKIPTOKEN },
|
||||
{ URI_ACTION_COOL_PRIM, QO_TOP },
|
||||
// ComplexReturnType
|
||||
{ URI_ACTION_CT, QO_FORMAT }, { URI_ACTION_CT, QO_SELECT }, { URI_ACTION_CT, QO_EXPAND },
|
||||
// ComplexCollectionReturnType
|
||||
{ URI_ACTION_COLL_CT, QO_FILTER }, { URI_ACTION_COLL_CT, QO_FORMAT },
|
||||
{ URI_ACTION_COLL_CT, QO_EXPAND }, { URI_ACTION_COLL_CT, QO_COUNT },
|
||||
{ URI_ACTION_COLL_CT, QO_ORDERBY }, { URI_ACTION_COLL_CT, QO_SELECT },
|
||||
{ URI_ACTION_COLL_CT, QO_SKIP }, { URI_ACTION_COLL_CT, QO_SKIPTOKEN },
|
||||
{ URI_ACTION_COLL_CT, QO_TOP },
|
||||
// EntityReturnType
|
||||
{ URI_ACTION_ENTITY, QO_FORMAT }, { URI_ACTION_ENTITY, QO_EXPAND }, { URI_ACTION_ENTITY, QO_SELECT },
|
||||
// EntityCollectionReturnType
|
||||
{ URI_ACTION_ES, QO_FORMAT }, { URI_ACTION_ES, QO_FILTER }, { URI_ENTITY_SET, URI_ACTION_ES },
|
||||
{ URI_ACTION_ES, QO_COUNT }, { URI_ACTION_ES, QO_ORDERBY }, /* { URI_ACTION_ES, QO_SEARCH }, */
|
||||
{ URI_ACTION_ES, QO_SELECT }, { URI_ACTION_ES, QO_SKIP }, { URI_ACTION_ES, QO_SKIPTOKEN },
|
||||
{ URI_ACTION_ES, QO_TOP },
|
||||
};
|
||||
|
||||
private final String[][] actionsWithNotValidSystemQueryOptions = {
|
||||
// NoReturnType
|
||||
{ URI_ACTION_NO_RETURN, QO_FILTER }, { URI_ACTION_NO_RETURN, QO_ID },
|
||||
{ URI_ACTION_NO_RETURN, QO_EXPAND }, { URI_ACTION_NO_RETURN, QO_COUNT },
|
||||
{ URI_ACTION_NO_RETURN, QO_ORDERBY },/* { URI_ACTION_NO_RETURN, QO_SEARCH }, */
|
||||
{ URI_ACTION_NO_RETURN, QO_SELECT }, { URI_ACTION_NO_RETURN, QO_SKIP },
|
||||
{ URI_ACTION_NO_RETURN, QO_SKIPTOKEN }, { URI_ACTION_NO_RETURN, QO_TOP },
|
||||
// PrimReturnType
|
||||
{ URI_ACTION_PRIM, QO_FILTER }, { URI_ACTION_PRIM, QO_ID },
|
||||
{ URI_ACTION_PRIM, QO_EXPAND }, { URI_ACTION_PRIM, QO_COUNT },
|
||||
{ URI_ACTION_PRIM, QO_ORDERBY },/* { URI_ACTION_PRIM, QO_SEARCH }, */
|
||||
{ URI_ACTION_PRIM, QO_SELECT }, { URI_ACTION_PRIM, QO_SKIP },
|
||||
{ URI_ACTION_PRIM, QO_SKIPTOKEN }, { URI_ACTION_PRIM, QO_TOP },
|
||||
// PrimCollectionReturnType
|
||||
{ URI_ACTION_COOL_PRIM, QO_ID }, { URI_ACTION_COOL_PRIM, QO_EXPAND },
|
||||
/* { URI_ACTION_COOL_PRIM, QO_SEARCH }, */{ URI_ACTION_COOL_PRIM, QO_SELECT },
|
||||
// ComplexReturnType
|
||||
{ URI_ACTION_CT, QO_FILTER }, { URI_ACTION_CT, QO_ID }, { URI_ACTION_CT, QO_COUNT },
|
||||
{ URI_ACTION_CT, QO_ORDERBY }, /* { URI_ACTION_CT, QO_SEARCH }, */
|
||||
{ URI_ACTION_CT, QO_SKIP }, { URI_ACTION_CT, QO_SKIPTOKEN }, { URI_ACTION_CT, QO_TOP },
|
||||
// ComplexCollectionReturnType
|
||||
{ URI_ACTION_COLL_CT, QO_ID },
|
||||
/* { URI_ACTION_COLL_CT, QO_SEARCH }, */
|
||||
// EntityReturnType
|
||||
{ URI_ACTION_ENTITY, QO_FILTER }, { URI_ACTION_ENTITY, QO_ID }, { URI_ACTION_ENTITY, QO_COUNT },
|
||||
/* { URI_ACTION_ENTITY, QO_ORDERBY }, *//* { URI_ACTION_ENTITY, QO_SEARCH }, */
|
||||
{ URI_ACTION_ENTITY, QO_SKIP }, { URI_ACTION_ENTITY, QO_SKIPTOKEN }, { URI_ACTION_ENTITY, QO_TOP },
|
||||
// EntityCollectionReturnType
|
||||
{ URI_ACTION_ES, QO_ID },
|
||||
};
|
||||
|
||||
private static final Edm edm = new EdmProviderImpl(new EdmTechProvider());
|
||||
|
||||
@Test
|
||||
public void validatePostOnActionSystemQueryOptions() throws Exception {
|
||||
for (final String[] uriArray : actionWithValidSystemQueryOptions) {
|
||||
final String[] uri = constructUri(uriArray);
|
||||
try {
|
||||
new UriValidator().validate(
|
||||
new Parser().parseUri(uri[0], uri[1], null, edm),
|
||||
HttpMethod.GET);
|
||||
} catch (final UriParserException e) {
|
||||
fail("Failed for uri: " + uri[0] + '?' + uri[1]);
|
||||
} catch (final UriValidationException e) {
|
||||
fail("Failed for uri: " + uri[0] + '?' + uri[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkPostOnActionSystemQueryOptionsNotValid() throws Exception {
|
||||
for (final String[] uriArray : actionsWithNotValidSystemQueryOptions) {
|
||||
final String[] uri = constructUri(uriArray);
|
||||
validateWrong(uri[0], uri[1], HttpMethod.POST,
|
||||
UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serviceDocumentMustNotFailForHttpPostPutPatchDelete() throws Exception {
|
||||
// We must not fail with a nullpointer here as the HTTP method to resource validation happens in the dispatcher
|
||||
final UriInfo uri = new Parser().parseUri(URI_SERVICE, null, null, edm);
|
||||
final UriValidator validator = new UriValidator();
|
||||
|
||||
validator.validate(uri, HttpMethod.GET);
|
||||
validator.validate(uri, HttpMethod.POST);
|
||||
validator.validate(uri, HttpMethod.PUT);
|
||||
validator.validate(uri, HttpMethod.DELETE);
|
||||
validator.validate(uri, HttpMethod.PATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateForHttpMethods() throws Exception {
|
||||
final UriInfo uri = new Parser().parseUri(URI_ENTITY, null, null, edm);
|
||||
|
@ -277,6 +379,81 @@ public class UriValidatorTest {
|
|||
validator.validate(uri, HttpMethod.PATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemQueryOptionsNotAllowedForHttpPostPutPatchDelete() throws Exception {
|
||||
String[] queryOptions =
|
||||
{ QO_FILTER, QO_FORMAT, QO_EXPAND, QO_COUNT, QO_ORDERBY, QO_SELECT, QO_SKIP, QO_TOP, QO_SKIPTOKEN };
|
||||
final UriValidator validator = new UriValidator();
|
||||
for (int i = 0; i < queryOptions.length; i++) {
|
||||
final UriInfo uri = new Parser().parseUri(URI_ENTITY, queryOptions[i], null, edm);
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.POST);
|
||||
fail("UriValidation exception expected for method POST and system query option: " + queryOptions[i]);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.PUT);
|
||||
fail("UriValidation exception expected for method PUT and system query option: " + queryOptions[i]);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.PATCH);
|
||||
fail("UriValidation exception expected for method PATCH and system query option: " + queryOptions[i]);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.DELETE);
|
||||
fail("UriValidation exception expected for method DELETE and system query option: " + queryOptions[i]);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemQueryOptionIDAllowedForDELETEReferencesOnly() throws Exception {
|
||||
final UriValidator validator = new UriValidator();
|
||||
final UriInfo uri = new Parser().parseUri(URI_REFERENCES, QO_ID, null, edm);
|
||||
validator.validate(uri, HttpMethod.DELETE);
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.POST);
|
||||
fail("UriValidation exception expected for method POST and system query option: " + QO_ID);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.PUT);
|
||||
fail("UriValidation exception expected for method PUT and system query option: " + QO_ID);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
try {
|
||||
validator.validate(uri, HttpMethod.PATCH);
|
||||
fail("UriValidation exception expected for method PATCH and system query option: " + QO_ID);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
|
||||
final UriInfo newUri = new Parser().parseUri(URI_ENTITY, QO_ID, null, edm);
|
||||
try {
|
||||
validator.validate(newUri, HttpMethod.DELETE);
|
||||
fail("UriValidation exception expected for method DELETE and system query option: " + QO_ID);
|
||||
} catch (UriValidationException e) {
|
||||
assertEquals(UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, e
|
||||
.getMessageKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateSelect() throws Exception {
|
||||
new TestUriValidator().setEdm(edm).run(URI_ENTITY, "$select=PropertyString");
|
||||
|
|
Loading…
Reference in New Issue