-
Notifications
You must be signed in to change notification settings - Fork 230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 1197 add memberof operator #1291
Conversation
@@ -59,6 +59,12 @@ public static boolean toManyInPathExceptLastPathElement(EntityDictionary diction | |||
.anyMatch(RelationshipType::isToMany); | |||
} | |||
|
|||
public static boolean checkLastPathElementType(EntityDictionary dictionary, Path path, Class<?> clz) { | |||
int pathLength = path.getPathElements().size(); | |||
PathElement lastPathElement = path.getPathElements().get(pathLength - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a method in Path to get the last element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check out in the same file:
public static boolean toManyInPath(EntityDictionary dictionary, Path path) {
return path.getPathElements().stream()
.map(element -> dictionary.getRelationshipType(element.getType(), element.getFieldName()))
.anyMatch(RelationshipType::isToMany);
}
public static boolean toManyInPathExceptLastPathElement(EntityDictionary dictionary, Path path) {
int pathLength = path.getPathElements().size();
return path.getPathElements().stream()
.limit(pathLength - 1)
.map(element -> dictionary.getRelationshipType(element.getType(), element.getFieldName()))
.anyMatch(RelationshipType::isToMany);
}
Let's add a method that looks like one of these (toManyInLastPathElement):
throw new InvalidPredicateException("HasMember can only take one argument"); | ||
} | ||
Object val = getFieldValue(entity, field, requestScope); | ||
String filterStr = CoerceUtil.coerce(values.get(0), String.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
coerce into val.getClass(). We probably want to support lists of numbers too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Object val
will be an instance of Collection. To get the parameterized type of collection, I think we need to iterate over the path and get paremeterizedType from entityDict. I am not sure how to get parameterized type from Collection object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, we'll need to iterate over it.
Another alternative is to convert field into a Path object in Elide. That will do all the type conversion for you.
@@ -111,12 +112,19 @@ public FilterExpression parseGlobalExpression(String path, MultivaluedMap<String | |||
throw new ParseException(String.format("Invalid predicate: %s", filterPredicate)); | |||
} | |||
|
|||
if ((filterPredicate.getOperator().equals(Operator.HASMEMBER) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems redundant with the validation logic below. Do we also need this check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is for Global Filter. The validation logic below is for the typed filter. Previously we did not have toMany
relationship check for global filter. So I decided to include only Iscollection check for global filter.
Without this check in global filter, we allow filter expr like book?filter=title=hasmember='1'
to pass through and jpql rejects these statement.
@@ -170,6 +172,21 @@ | |||
return String.format("%s IS NOT EMPTY", columnAlias); | |||
}); | |||
|
|||
operatorGenerators.put(HASMEMBER, (columnAlias, params) -> { | |||
Preconditions.checkArgument(params.size() == 1); | |||
String x = String.format("%s MEMBER OF %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove the local variable here?
@@ -40,7 +40,7 @@ | |||
|
|||
<properties> | |||
<dataStoreHarness>com.yahoo.elide.datastores.hibernate3.HibernateDataStoreHarness</dataStoreHarness> | |||
<excludeTags>emptyOnAttributeCollection</excludeTags> | |||
<excludeTags>emptyOnAttributeCollection,memberOfOperation</excludeTags> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Elide 5 - we make tags for stores (rather than features).
result = getAsNode(String.format("/book?filter[book.awards][hasmember]=%s", filterString)); | ||
assertEquals(awardBook.size(), result.get("data").size()); | ||
for (JsonNode book : result.get("data")) { | ||
assertTrue(awardBook.contains(book.get("id"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a much cleaner way to do these comparisons with hamcrest Matchers. There are some good examples in PaginationIT.
f9833ba
to
c9b9244
Compare
@@ -59,6 +59,15 @@ public static boolean toManyInPathExceptLastPathElement(EntityDictionary diction | |||
.anyMatch(RelationshipType::isToMany); | |||
} | |||
|
|||
public static boolean checkLastPathElementType(EntityDictionary dictionary, Path path, Class<?> clz) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this method to something more specific about what it's checking?
How about isLastPathElementAssignableFrom
throw new InvalidPredicateException("HasMember can only take one argument"); | ||
} | ||
Object val = getFieldValue(entity, field, requestScope); | ||
String filterStr = CoerceUtil.coerce(values.get(0), String.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, we'll need to iterate over it.
Another alternative is to convert field into a Path object in Elide. That will do all the type conversion for you.
throw new InvalidPredicateException("HasMember can only take one argument"); | ||
} | ||
Object val = getFieldValue(entity, fieldPath, requestScope); | ||
String filterStr = CoerceUtil.coerce(values.get(0), String.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need to fix the coercion.
|
||
@Test | ||
@Tag("excludeOnHibernate3") | ||
void testExceptionOnMemberOfOperator() throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add error tests for book.authors and book.publisher?
7257fa0
to
43ced02
Compare
Updated CVSS limit to 7 to unblock release.
Resolves #1197
Description
Add the
member of
operation for only Element Collection attributes for root entity or toOne relationship entity.filter[book]=awards=hasmember='Booker Prize'
filter[book]=publisher.phoneNumbers=hasmember='999-999-9999'
Enables to filter on collection attribute
License
I confirm that this contribution is made under an Apache 2.0 license and that I have the authority necessary to make this contribution on behalf of its copyright owner.