[OLINGO-206] introduce decision matrix

This commit is contained in:
Stephan Klevenz 2014-03-17 17:14:27 +01:00
parent df675e15af
commit c0db941b85
6 changed files with 254 additions and 120 deletions

View File

@ -18,6 +18,11 @@
*/
package org.apache.olingo.server.api.uri;
import java.util.Collection;
import java.util.List;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
/**
* Object acting as general access to URI information extracted from the request URI. Depending on
* the URI info kind different interfaces are used to provide access to that information. </p>
@ -43,4 +48,6 @@ public interface UriInfo extends
public UriInfoResource asUriInfoResource();
public Collection<SystemQueryOption> getSystemQueryOptions();
}

View File

@ -19,9 +19,12 @@
package org.apache.olingo.server.core.uri;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriInfoAll;
@ -44,22 +47,12 @@ import org.apache.olingo.server.api.uri.queryoption.SearchOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.api.uri.queryoption.SkipOption;
import org.apache.olingo.server.api.uri.queryoption.SkipTokenOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
import org.apache.olingo.server.api.uri.queryoption.TopOption;
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.OrderByOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.QueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.SystemQueryOptionImpl;
import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
public class UriInfoImpl implements UriInfo {
@ -68,19 +61,10 @@ public class UriInfoImpl implements UriInfo {
private List<String> entitySetNames = new ArrayList<String>(); // for $entity
private EdmEntityType entityTypeCast; // for $entity
// Query options
private List<CustomQueryOptionImpl> customQueryOptions = new ArrayList<CustomQueryOptionImpl>();
private ExpandOptionImpl expandOption;
private FilterOptionImpl filterOption;
private FormatOptionImpl formatOption;
private IdOption idOption;
private CountOptionImpl inlineCountOption;
private OrderByOptionImpl orderByOption;
private SearchOptionImpl searchOption;
private SelectOptionImpl selectOption;
private SkipOptionImpl skipOption;
private SkipTokenOptionImpl skipTokenOption;
private TopOptionImpl topOption;
HashMap<SystemQueryOptionKind, SystemQueryOption> systemQueryOptions =
new HashMap<SystemQueryOptionKind, SystemQueryOption>();
private String fragment;
@ -162,27 +146,27 @@ public class UriInfoImpl implements UriInfo {
@Override
public ExpandOption getExpandOption() {
return expandOption;
return (ExpandOption) systemQueryOptions.get(SystemQueryOptionKind.EXPAND);
}
@Override
public FilterOption getFilterOption() {
return filterOption;
return (FilterOption) systemQueryOptions.get(SystemQueryOptionKind.FILTER);
}
@Override
public FormatOption getFormatOption() {
return formatOption;
return (FormatOption) systemQueryOptions.get(SystemQueryOptionKind.FORMAT);
}
@Override
public IdOption getIdOption() {
return idOption;
return (IdOption) systemQueryOptions.get(SystemQueryOptionKind.ID);
}
@Override
public CountOption getCountOption() {
return inlineCountOption;
return (CountOption) systemQueryOptions.get(SystemQueryOptionKind.COUNT);
}
@Override
@ -201,33 +185,33 @@ public class UriInfoImpl implements UriInfo {
@Override
public OrderByOption getOrderByOption() {
return orderByOption;
return (OrderByOption) systemQueryOptions.get(SystemQueryOptionKind.ORDERBY);
}
@Override
public SearchOption getSearchOption() {
return searchOption;
return (SearchOption) systemQueryOptions.get(SystemQueryOptionKind.SEARCH);
}
@Override
public SelectOption getSelectOption() {
return selectOption;
return (SelectOption) systemQueryOptions.get(SystemQueryOptionKind.SELECT);
}
@Override
public SkipOption getSkipOption() {
return skipOption;
return (SkipOption) systemQueryOptions.get(SystemQueryOptionKind.SKIP);
}
@Override
public SkipTokenOption getSkipTokenOption() {
return skipTokenOption;
return (SkipTokenOption) systemQueryOptions.get(SystemQueryOptionKind.SKIPTOKEN);
}
@Override
public TopOption getTopOption() {
return topOption;
return (TopOption) systemQueryOptions.get(SystemQueryOptionKind.TOP);
}
public UriInfoImpl setQueryOptions(final List<QueryOptionImpl> list) {
@ -249,28 +233,33 @@ public class UriInfoImpl implements UriInfo {
public UriInfoImpl setSystemQueryOption(final SystemQueryOptionImpl systemOption) {
if (systemOption.getKind() == SystemQueryOptionKind.EXPAND) {
expandOption = (ExpandOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.EXPAND, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.FILTER) {
filterOption = (FilterOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.FILTER, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.FORMAT) {
formatOption = (FormatOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.FORMAT, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.ID) {
idOption = (IdOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.ID, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.COUNT) {
inlineCountOption = (CountOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.COUNT, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.ORDERBY) {
orderByOption = (OrderByOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.ORDERBY, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.SEARCH) {
searchOption = (SearchOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.SEARCH, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.SELECT) {
selectOption = (SelectOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.SELECT, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.SKIP) {
skipOption = (SkipOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.SKIP, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.SKIPTOKEN) {
skipTokenOption = (SkipTokenOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.SKIPTOKEN, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.TOP) {
topOption = (TopOptionImpl) systemOption;
systemQueryOptions.put(SystemQueryOptionKind.TOP, systemOption);
} else if (systemOption.getKind() == SystemQueryOptionKind.LEVELS) {
systemQueryOptions.put(SystemQueryOptionKind.LEVELS, systemOption);
} else {
throw new ODataRuntimeException("Unsupported System Query Option: " + systemOption.getName());
}
return this;
}
@ -291,6 +280,10 @@ public class UriInfoImpl implements UriInfo {
public void removeResourcePart(int index) {
this.pathParts.remove(index);
}
@Override
public Collection<SystemQueryOption> getSystemQueryOptions() {
return Collections.unmodifiableCollection(systemQueryOptions.values());
}
}

View File

@ -0,0 +1,159 @@
/*
* 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.olingo.server.core.uri.validator;
import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
public class SystemQueryValidator {
//@formatter:off (Eclipse formatter)
//CHECKSTYLE:OFF (Maven checkstyle)
private boolean[][] decisionMatrix =
{
/* FILTER FORMAT EXPAND ID COUNT ORDERBY SEARCH SELECT SKIP SKIPTOKEN LEVELS TOP */
/* all */ { true , true , true , false, true , true , true , true , true , true , true , false },
/* batch */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* crossjoin */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entityId */ { false, true , true , true , false, false, false, true , false, false, true , false },
/* metadata */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* resource */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* service */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* entitySet */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entitySetCount */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* entity */ { false, true , true , false, false, false, false, true , false, false, true , false },
/* mediaStream */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* references */ { true , true , false, false, false, true , true , false, true , true , false, true },
/* reference */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyComplex */ { false, true , true , false, false, false, false, true , false, false, true , false },
/* propertyComplexCollection */ { true , true , true , false, true , true , false, false, true , true , true , true },
/* propertyComplexCollectionCount */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitive */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveCollection */ { true , true , false, false, false, true , false, false, true , true , false, true },
/* propertyPrimitiveCollectionCount */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveValue */ { false, true , false, false, false, false, false, false, false, false, false, false },
};
//CHECKSTYLE:ON
//@formatter:on
public void validate(final UriInfo uriInfo, final Edm edm) throws UriValidationException {
validateQueryOptions(uriInfo);
validateKeyPredicateTypes(uriInfo, edm);
}
private int colIndex(SystemQueryOptionKind queryOptionKind) {
int idx;
switch (queryOptionKind) {
case FILTER:
idx = 0;
break;
case FORMAT:
idx = 1;
break;
case EXPAND:
idx = 2;
break;
case ID:
idx = 3;
break;
case COUNT:
idx = 4;
break;
case ORDERBY:
idx = 5;
break;
case SEARCH:
idx = 6;
break;
case SELECT:
idx = 7;
break;
case SKIP:
idx = 8;
break;
case SKIPTOKEN:
idx = 9;
break;
case LEVELS:
idx = 10;
break;
case TOP:
idx = 11;
break;
default:
throw new ODataRuntimeException("Unsupported Option: " + queryOptionKind);
}
return idx;
}
private int rowIndex(final UriInfo uriInfo) {
int idx;
switch (uriInfo.getKind()) {
case all:
idx = 0;
break;
case batch:
idx = 1;
break;
case crossjoin:
idx = 2;
break;
case entityId:
idx = 3;
break;
case metadata:
idx = 4;
break;
case resource:
idx = 5;
break;
case service:
idx = 6;
break;
default:
throw new ODataRuntimeException("Unsupported Option: " + uriInfo.getKind());
}
return idx;
}
private void validateKeyPredicateTypes(final UriInfo uriInfo, final Edm edm) throws UriValidationException {
}
private void validateQueryOptions(final UriInfo uriInfo) throws UriValidationException {
int row = rowIndex(uriInfo);
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
int col = colIndex(option.getKind());
if (!decisionMatrix[row][col]) {
throw new UriValidationException("Unsupported System Query Option for Uri Type: " + option.getName());
}
}
}
}

View File

@ -20,6 +20,10 @@ package org.apache.olingo.server.core.uri.validator;
public class UriValidationException extends Exception {
public UriValidationException(String msg) {
super(msg);
}
private static final long serialVersionUID = -3179078078053564742L;
}

View File

@ -1,37 +0,0 @@
/*
* 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.olingo.server.core.uri.validator;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.server.api.uri.UriInfo;
public class Validator {
public void validate(UriInfo uriInfo, Edm edm) throws UriValidationException {
switch (uriInfo.getKind()) {
case metadata:
if (uriInfo.getTopOption() != null) {
throw new UriValidationException();
}
break;
default:
}
}
}

View File

@ -18,25 +18,23 @@
*/
package org.apache.olingo.server.core.uri.validator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.core.edm.provider.EdmProviderImpl;
import org.apache.olingo.server.core.testutil.EdmTechProvider;
import org.apache.olingo.server.core.uri.parser.Parser;
import org.apache.olingo.server.core.uri.parser.RawUri;
import org.apache.olingo.server.core.uri.parser.UriDecoder;
import org.apache.olingo.server.core.uri.parser.UriParserException;
import org.junit.Ignore;
import org.junit.Test;
public class UriEdmValidatorTest {
private Edm edm = new EdmProviderImpl(new EdmTechProvider());
String[] uris = {
String[] tmpUri = {
"$crossjoin(ESKeyNav, ESTwoKeyNav)/invalid ",
"$crossjoin(invalidEntitySet) ",
"$entity ",
@ -50,26 +48,17 @@ public class UriEdmValidatorTest {
"FICRTESTwoKeyNavParam(ParameterInt16=@invalidAlias)?@validAlias=1 ",
"FINRTESMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='2')/PropertyComplex ",
"FINRTESMixPrimCollCompTwoParam(ParameterInt16=1,ParameterString='2')/$count ",
"ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$ref ",
"ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$count ",
"ESKeyNav?$top=-3 ",
"ESAllPrim?$count=foo ",
"ESAllPrim?$skip=-3 "
// "ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$ref ",
// "ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$count ",
// "ESKeyNav?$top=-3 ",
// "ESAllPrim?$count=foo ",
// "ESAllPrim?$skip=-3 "
};
@Test
public void foo() throws Exception {
for (String uri : uris) {
Parser parser = new Parser();
System.out.println(uri);
UriInfo uriInfo = parser.parseUri(uri.trim(), edm);
assertNotNull(uriInfo);
}
}
@Test
@Ignore("key predicate validation not implemented")
public void keyPredicateValidTypes() throws Exception {
String[] uris = { "/ESAllPrim" };
String[] uris = {};
for (String uri : uris) {
parseAndValidate(uri);
@ -78,6 +67,7 @@ public class UriEdmValidatorTest {
}
@Test
@Ignore("key predicate validation not implemented")
public void keyPredicateInvalidTypes() throws UriParserException {
String[] uris = {};
@ -94,21 +84,39 @@ public class UriEdmValidatorTest {
@Test
public void systemQueryOptionValid() throws Exception {
String[] uris = {
String[] uris =
{
/* service document */
"",
/* metadata */
"/$metadata",
"/$metadata?$format=json"
"/$metadata?$format=atom",
};
for (String uri : uris) {
try {
parseAndValidate(uri);
} catch (Exception e) {
throw new Exception("Faild for uri: " + uri, e);
}
}
}
@Test
public void systemQueryOptionInvalid() throws Exception {
String[] uris = {
"/$metadata?$format=json&$top=3"
String[] uris =
{
/* service document */
/* metadata */
"/$metadata?$format=json&$top=3&$skip=5&$skiptoken=123",
/* misc */
"ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$ref ",
"ESKeyNav(1)?$expand=NavPropertyETKeyNavOne/$count ",
"ESKeyNav?$top=-3 ",
"ESAllPrim?$count=foo ",
"ESAllPrim?$skip=-3 "
};
for (String uri : uris) {
@ -124,7 +132,7 @@ public class UriEdmValidatorTest {
private void parseAndValidate(String uri) throws UriParserException, UriValidationException {
UriInfo uriInfo = new Parser().parseUri(uri.trim(), edm);
Validator validator = new Validator();
SystemQueryValidator validator = new SystemQueryValidator();
validator.validate(uriInfo, edm);
}
}