$filter $top $skip $count logic
implemented eq and ne (case-sensitive) for String values, and eq, ne, gt ,ge , lt, le on the Timestamp field
This commit is contained in:
parent
091af5df2d
commit
5ae46e1f4b
|
@ -18,7 +18,10 @@ import org.apache.olingo.server.api.serializer.SerializerResult;
|
|||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
|
||||
import org.apache.olingo.server.api.uri.queryoption.*;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.reso.service.data.meta.FieldInfo;
|
||||
import org.reso.service.data.meta.FilterExpressionVisitor;
|
||||
import org.reso.service.data.meta.ResourceInfo;
|
||||
import org.reso.service.edmprovider.RESOedmProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -30,6 +33,7 @@ import java.net.URISyntaxException;
|
|||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class GenericEntityCollectionProcessor implements EntityCollectionProcessor
|
||||
|
@ -82,9 +86,18 @@ public class GenericEntityCollectionProcessor implements EntityCollectionProcess
|
|||
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); // in our example, the first segment is the EntitySet
|
||||
EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
|
||||
|
||||
boolean isCount = false;
|
||||
CountOption countOption = uriInfo.getCountOption();
|
||||
if (countOption != null) {
|
||||
isCount = countOption.getValue();
|
||||
if (isCount){
|
||||
LOG.info("Count str:"+countOption.getText() );
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd: fetch the data from backend for this requested EntitySetName
|
||||
// it has to be delivered as EntitySet object
|
||||
EntityCollection entitySet = getData(edmEntitySet);
|
||||
EntityCollection entitySet = getData(edmEntitySet, uriInfo, isCount);
|
||||
|
||||
// 3rd: create a serializer based on the requested format (json)
|
||||
try
|
||||
|
@ -105,7 +118,19 @@ public class GenericEntityCollectionProcessor implements EntityCollectionProcess
|
|||
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build();
|
||||
|
||||
final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName();
|
||||
EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with().id(id).contextURL(contextUrl).build();
|
||||
EntityCollectionSerializerOptions opts = null;
|
||||
if (isCount) // If there's a $count=true in the query string, we need to have a different formatting options.
|
||||
{
|
||||
opts = EntityCollectionSerializerOptions.with()
|
||||
.contextURL(contextUrl)
|
||||
.id(id)
|
||||
.count(countOption)
|
||||
.build();
|
||||
}
|
||||
else
|
||||
{
|
||||
opts = EntityCollectionSerializerOptions.with().id(id).contextURL(contextUrl).build();
|
||||
}
|
||||
SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, entitySet, opts);
|
||||
InputStream serializedContent = serializerResult.getContent();
|
||||
|
||||
|
@ -116,20 +141,85 @@ public class GenericEntityCollectionProcessor implements EntityCollectionProcess
|
|||
}
|
||||
|
||||
|
||||
protected EntityCollection getData(EdmEntitySet edmEntitySet){
|
||||
protected EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo, boolean isCount) throws ODataApplicationException {
|
||||
ArrayList<FieldInfo> fields = this.resourceInfo.getFieldList();
|
||||
|
||||
EntityCollection lookupsCollection = new EntityCollection();
|
||||
EntityCollection entCollection = new EntityCollection();
|
||||
|
||||
List<Entity> productList = lookupsCollection.getEntities();
|
||||
List<Entity> productList = entCollection.getEntities();
|
||||
|
||||
Map<String, String> properties = System.getenv();
|
||||
|
||||
try {
|
||||
|
||||
FilterOption filter = uriInfo.getFilterOption();
|
||||
String sqlCriteria = null;
|
||||
if (filter!=null)
|
||||
{
|
||||
sqlCriteria = filter.getExpression().accept(new FilterExpressionVisitor(this.resourceInfo));
|
||||
}
|
||||
|
||||
// Statements allow to issue SQL queries to the database
|
||||
Statement statement = connect.createStatement();
|
||||
// Result set get the result of the SQL query
|
||||
ResultSet resultSet = statement.executeQuery("select * from "+this.resourceInfo.getTableName());
|
||||
String queryString = null;
|
||||
|
||||
// Logic for $count
|
||||
if (isCount)
|
||||
{
|
||||
queryString = "select count(*) AS rowcount from " + this.resourceInfo.getTableName();
|
||||
}
|
||||
else
|
||||
{
|
||||
queryString = "select * from " + this.resourceInfo.getTableName();
|
||||
}
|
||||
if (null!=sqlCriteria && sqlCriteria.length()>0)
|
||||
{
|
||||
queryString = queryString + " WHERE " + sqlCriteria;
|
||||
}
|
||||
|
||||
// Logic for $top
|
||||
TopOption topOption = uriInfo.getTopOption();
|
||||
if (topOption != null) {
|
||||
int topNumber = topOption.getValue();
|
||||
if (topNumber >= 0)
|
||||
{
|
||||
// Logic for $skip
|
||||
SkipOption skipOption = uriInfo.getSkipOption();
|
||||
if (skipOption != null)
|
||||
{
|
||||
int skipNumber = skipOption.getValue();
|
||||
if (skipNumber >= 0)
|
||||
{
|
||||
queryString = queryString + " LIMIT "+skipNumber+", "+topNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ODataApplicationException("Invalid value for $skip", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryString = queryString + " LIMIT " + topNumber;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ODataApplicationException("Invalid value for $top", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("SQL Query: "+queryString);
|
||||
ResultSet resultSet = statement.executeQuery(queryString);
|
||||
|
||||
// special return logic for $count
|
||||
if (isCount && resultSet.next())
|
||||
{
|
||||
int size = resultSet.getInt("rowcount");
|
||||
LOG.info("Size = "+size);
|
||||
entCollection.setCount(size);
|
||||
return entCollection;
|
||||
}
|
||||
|
||||
String primaryFieldName = fields.get(0).getFieldName();
|
||||
|
||||
|
@ -149,7 +239,7 @@ public class GenericEntityCollectionProcessor implements EntityCollectionProcess
|
|||
}
|
||||
else if (field.getType().equals(EdmPrimitiveTypeKind.DateTimeOffset.getFullQualifiedName()))
|
||||
{
|
||||
value = resultSet.getDate(fieldName);
|
||||
value = resultSet.getTimestamp(fieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -167,10 +257,10 @@ public class GenericEntityCollectionProcessor implements EntityCollectionProcess
|
|||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Server Error occurred in reading "+this.resourceInfo.getResourceName(), e);
|
||||
return lookupsCollection;
|
||||
return entCollection;
|
||||
}
|
||||
|
||||
return lookupsCollection;
|
||||
return entCollection;
|
||||
}
|
||||
|
||||
private URI createId(String entitySetName, Object id) {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
package org.reso.service.data.meta;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmEnumType;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.commons.api.http.HttpStatusCode;
|
||||
import org.apache.olingo.server.api.ODataApplicationException;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Member;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* $filter
|
||||
*/
|
||||
public class FilterExpressionVisitor implements ExpressionVisitor<String> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FilterExpressionVisitor.class);
|
||||
private static final Map<BinaryOperatorKind, String> BINARY_OPERATORS = new HashMap<BinaryOperatorKind, String>() {{
|
||||
put(BinaryOperatorKind.ADD, " + ");
|
||||
put(BinaryOperatorKind.AND, " AND ");
|
||||
put(BinaryOperatorKind.DIV, " / ");
|
||||
put(BinaryOperatorKind.EQ, " = ");
|
||||
put(BinaryOperatorKind.GE, " >= ");
|
||||
put(BinaryOperatorKind.GT, " > ");
|
||||
put(BinaryOperatorKind.LE, " <= ");
|
||||
put(BinaryOperatorKind.LT, " < ");
|
||||
put(BinaryOperatorKind.MOD, " % ");
|
||||
put(BinaryOperatorKind.MUL, " * ");
|
||||
put(BinaryOperatorKind.NE, " <> ");
|
||||
put(BinaryOperatorKind.OR, " OR ");
|
||||
put(BinaryOperatorKind.SUB, " - ");
|
||||
}};
|
||||
|
||||
private String entityAlias;
|
||||
private ResourceInfo resourceInfo;
|
||||
|
||||
public FilterExpressionVisitor(ResourceInfo resourceInfo) {
|
||||
this.entityAlias = resourceInfo.getTableName();
|
||||
this.resourceInfo = resourceInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitBinaryOperator(BinaryOperatorKind operator, String left, String right)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
String strOperator = BINARY_OPERATORS.get(operator);
|
||||
|
||||
if (strOperator == null) {
|
||||
throw new ODataApplicationException("Unsupported binary operation: " + operator.name(),
|
||||
operator == BinaryOperatorKind.HAS ?
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode() :
|
||||
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
return left + strOperator + right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitUnaryOperator(UnaryOperatorKind operator, String operand)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
switch (operator) {
|
||||
case NOT:
|
||||
return "NOT " + operand;
|
||||
case MINUS:
|
||||
return "-" + operand;
|
||||
}
|
||||
throw new ODataApplicationException("Wrong unary operator: " + operator,
|
||||
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMethodCall(MethodKind methodCall, List<String> parameters)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
if (parameters.isEmpty() && methodCall.equals(MethodKind.NOW)) {
|
||||
return "CURRENT_DATE";
|
||||
}
|
||||
String firsEntityParam = parameters.get(0);
|
||||
switch (methodCall) {
|
||||
case CONTAINS:
|
||||
return firsEntityParam + " LIKE '%" + extractFromStringValue(parameters.get(1)) + "%'";
|
||||
case STARTSWITH:
|
||||
return firsEntityParam + " LIKE '" + extractFromStringValue(parameters.get(1)) + "%'";
|
||||
case ENDSWITH:
|
||||
return firsEntityParam + " LIKE '%" + extractFromStringValue(parameters.get(1));
|
||||
case DAY:
|
||||
return "DAY(" + firsEntityParam + ")";
|
||||
case MONTH:
|
||||
return "MONTH(" + firsEntityParam + ")";
|
||||
case YEAR:
|
||||
return "YEAR(" + firsEntityParam + ")";
|
||||
}
|
||||
throw new ODataApplicationException("Method call " + methodCall + " not implemented",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitLiteral(Literal literal) throws ExpressionVisitException, ODataApplicationException {
|
||||
String literalAsString = literal.getText();
|
||||
if (literal.getType() == null) {
|
||||
literalAsString = "NULL";
|
||||
}
|
||||
if (literal.getType().getFullQualifiedName().equals( EdmPrimitiveTypeKind.DateTimeOffset.getFullQualifiedName() ) )
|
||||
{
|
||||
return "'"+literalAsString+"'";
|
||||
}
|
||||
|
||||
return literalAsString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitMember(Member member) throws ExpressionVisitException, ODataApplicationException {
|
||||
List<UriResource> resources = member.getResourcePath().getUriResourceParts();
|
||||
|
||||
UriResource first = resources.get(0);
|
||||
|
||||
// TODO: Enum and ComplexType; joins
|
||||
if (resources.size() == 1 && first instanceof UriResourcePrimitiveProperty) {
|
||||
UriResourcePrimitiveProperty primitiveProperty = (UriResourcePrimitiveProperty) first;
|
||||
return entityAlias + "." + primitiveProperty.getProperty().getName();
|
||||
} else {
|
||||
throw new ODataApplicationException("Only primitive properties are implemented in filter expressions",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitEnum(EdmEnumType type, List<String> enumValues)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new ODataApplicationException("Enums are not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),
|
||||
Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitLambdaExpression(String lambdaFunction, String lambdaVariable, Expression expression)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new ODataApplicationException("Lambda expressions are not implemented",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitAlias(String aliasName) throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new ODataApplicationException("Aliases are not implemented",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitTypeLiteral(EdmType type) throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new ODataApplicationException("Type literals are not implemented",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitLambdaReference(String variableName) throws ExpressionVisitException, ODataApplicationException {
|
||||
throw new ODataApplicationException("Lambda references are not implemented",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private String extractFromStringValue(String val) {
|
||||
return val.substring(1, val.length() - 1);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ public class BasicAuthProvider implements Provider
|
|||
private static final Logger LOG = LoggerFactory.getLogger(BasicAuthProvider.class);
|
||||
|
||||
/**
|
||||
* A simple BEARER Token Auth with a set token.
|
||||
* A simple BASIC Auth with static username and password.
|
||||
* @param req The HTTP Request object from the servlet.
|
||||
* @return true if authorized, false otherwise.
|
||||
*/
|
||||
|
@ -38,7 +38,6 @@ public class BasicAuthProvider implements Provider
|
|||
if (authResp!=null && authResp.length()>0)
|
||||
{
|
||||
String[] parts = authResp.split(BasicAuthProvider.AUTH_SPACE);
|
||||
LOG.info("header:"+authResp);
|
||||
if (parts[0].equals(BasicAuthProvider.BASIC_STR) && parts.length==2)
|
||||
{
|
||||
String base64decoded = new String(Base64.getDecoder().decode(parts[1]));
|
||||
|
@ -48,8 +47,6 @@ public class BasicAuthProvider implements Provider
|
|||
{
|
||||
String username = parts[0];
|
||||
String password = parts[1];
|
||||
LOG.info("User:"+username);
|
||||
LOG.info("Pass:"+password);
|
||||
|
||||
if (username.equals(AUTH_USER) && password.equals(AUTH_PASSWORD))
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ public class BearerAuthProvider implements Provider
|
|||
private static final Logger LOG = LoggerFactory.getLogger(BearerAuthProvider.class);
|
||||
|
||||
/**
|
||||
* A simple BASIC Auth with static username and password. Purely for testing purposes.
|
||||
* A simple BEARER Token Auth with a set token.
|
||||
* @param req The HTTP Request object from the servlet.
|
||||
* @return true if authorized, false otherwise.
|
||||
*/
|
||||
|
|
|
@ -49,7 +49,7 @@ public class RESOservlet extends HttpServlet
|
|||
}
|
||||
|
||||
this.validator = new Validator();
|
||||
// this.validator.addProvider(new BasicAuthProvider());
|
||||
// this.validator.addProvider(new BasicAuthProvider()); // We're using this for the token auth. Only use here for easy browser testing.
|
||||
this.validator.addProvider(new BearerAuthProvider());
|
||||
|
||||
String mysqlHost = env.get("SQL_HOST");
|
||||
|
|
Loading…
Reference in New Issue