use of com.yahoo.elide.core.Path in project elide by yahoo.
the class DefaultFilterDialect method getPath.
/**
* Parses [ author, books, publisher, name ] into [(author, books), (book, publisher), (publisher, name)].
*
* @param keyParts [ author, books, publisher, name ]
* @param apiVersion The client requested version.
* @return [(author, books), (book, publisher), (publisher, name)]
* @throws ParseException if the filter cannot be parsed
*/
private Path getPath(final String[] keyParts, String apiVersion) throws ParseException {
if (keyParts == null || keyParts.length <= 0) {
throw new ParseException("Invalid filter expression");
}
List<Path.PathElement> path = new ArrayList<>();
Type<?>[] types = new Type[keyParts.length];
String type = keyParts[0];
types[0] = dictionary.getEntityClass(type, apiVersion);
if (types[0] == null) {
throw new ParseException("Unknown entity in filter: " + type);
}
/* Extract all the paths for the associations */
for (int i = 1; i < keyParts.length; ++i) {
final String field = keyParts[i];
final Type<?> entityClass = types[i - 1];
final Type<?> fieldType = ("id".equals(field.toLowerCase(Locale.ENGLISH))) ? dictionary.getIdType(entityClass) : dictionary.getParameterizedType(entityClass, field);
if (fieldType == null) {
throw new ParseException("Unknown field in filter: " + field);
}
types[i] = fieldType;
}
/* Build all the Predicate path elements */
for (int i = 0; i < types.length - 1; ++i) {
Type typeClass = types[i];
String fieldName = keyParts[i + 1];
Type fieldClass = types[i + 1];
Path.PathElement pathElement = new Path.PathElement(typeClass, fieldClass, fieldName);
path.add(pathElement);
}
return new Path(path);
}
use of com.yahoo.elide.core.Path in project elide by yahoo.
the class EntityProjectionMakerTest method testRelationshipsAndIncludeWithFilterAndSort.
@Test
public void testRelationshipsAndIncludeWithFilterAndSort() {
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<>();
queryParams.add("include", "authors");
queryParams.add("filter[author]", "name=='Foo'");
queryParams.add("filter[publisher]", "name=='Foo'");
queryParams.add("sort", "name");
String path = "/book/1/relationships/publisher";
Sorting sorting = SortingImpl.parseSortRule("name", ClassType.of(Publisher.class), dictionary);
RequestScope scope = new TestRequestScope(dictionary, path, queryParams);
EntityProjectionMaker maker = new EntityProjectionMaker(dictionary, scope);
EntityProjection expected = EntityProjection.builder().type(Book.class).relationship("publisher", EntityProjection.builder().type(Publisher.class).filterExpression(new InPredicate(new Path(Publisher.class, dictionary, "name"), "Foo")).sorting(sorting).pagination(PaginationImpl.getDefaultPagination(ClassType.of(Publisher.class))).build()).relationship("authors", EntityProjection.builder().attribute(Attribute.builder().name("name").type(String.class).build()).attribute(Attribute.builder().name("type").type(Author.AuthorType.class).build()).attribute(Attribute.builder().name("homeAddress").type(Address.class).build()).attribute(Attribute.builder().name("vacationHomes").type(Set.class).build()).attribute(Attribute.builder().name("stuff").type(Map.class).build()).attribute(Attribute.builder().name("awards").type(Collection.class).build()).filterExpression(new InPredicate(new Path(Author.class, dictionary, "name"), "Foo")).relationship("books", EntityProjection.builder().type(Book.class).build()).type(Author.class).build()).relationship("editor", EntityProjection.builder().type(Editor.class).build()).build();
EntityProjection actual = maker.parsePath(path);
projectionEquals(expected, actual);
}
use of com.yahoo.elide.core.Path in project elide by yahoo.
the class EntityProjectionMakerTest method testRootCollectionWithGlobalFilter.
@Test
public void testRootCollectionWithGlobalFilter() {
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<>();
queryParams.add("filter", "genre=='Science Fiction'");
String path = "/book";
RequestScope scope = new TestRequestScope(dictionary, path, queryParams);
FilterExpression expression = new InPredicate(new Path(Book.class, dictionary, "genre"), "Science Fiction");
EntityProjectionMaker maker = new EntityProjectionMaker(dictionary, scope);
EntityProjection expected = EntityProjection.builder().type(Book.class).attribute(Attribute.builder().name("language").type(String.class).build()).attribute(Attribute.builder().name("genre").type(String.class).build()).attribute(Attribute.builder().name("title").type(String.class).build()).attribute(Attribute.builder().name("awards").type(Collection.class).build()).attribute(Attribute.builder().name("publishDate").type(long.class).build()).attribute(Attribute.builder().name("authorTypes").type(Collection.class).build()).attribute(Attribute.builder().name("price").type(Price.class).build()).filterExpression(expression).relationship("publisher", EntityProjection.builder().type(Publisher.class).build()).relationship("editor", EntityProjection.builder().type(Editor.class).build()).relationship("authors", EntityProjection.builder().type(Author.class).build()).pagination(PaginationImpl.getDefaultPagination(ClassType.of(Book.class))).build();
EntityProjection actual = maker.parsePath(path);
projectionEquals(expected, actual);
}
use of com.yahoo.elide.core.Path in project elide by yahoo.
the class SortingImpl method getValidSortingRules.
/**
* Given the sorting rules validate sorting rules against the entities bound to the entityClass.
* @param entityClass The root class for sorting (eg. /book?sort=-title this would be package.Book)
* @param attributes The attributes that are being requested for the sorted model
* @param dictionary The elide entity dictionary
* @param <T> The entityClass
* @return The valid sorting rules - validated through the entity dictionary, or empty dictionary
* @throws InvalidValueException when sorting values are not valid for the jpa entity
*/
private <T> Map<Path, SortOrder> getValidSortingRules(final Type<T> entityClass, final Set<Attribute> attributes, final EntityDictionary dictionary) throws InvalidValueException {
Map<Path, SortOrder> returnMap = new LinkedHashMap<>();
for (Map.Entry<String, SortOrder> entry : replaceIdRule(dictionary.getIdFieldName(entityClass)).entrySet()) {
String dotSeparatedPath = entry.getKey();
SortOrder order = entry.getValue();
Path path;
if (dotSeparatedPath.contains(".")) {
// Creating a path validates that the dot separated path is valid.
path = new Path(entityClass, dictionary, dotSeparatedPath);
} else {
Attribute attribute = attributes.stream().filter(attr -> attr.getName().equals(dotSeparatedPath) || attr.getAlias().equals(dotSeparatedPath)).findFirst().orElse(null);
if (attribute == null) {
path = new Path(entityClass, dictionary, dotSeparatedPath);
} else {
path = new Path(entityClass, dictionary, attribute.getName(), attribute.getAlias(), attribute.getArguments());
}
}
if (!isValidSortRulePath(path, dictionary)) {
throw new InvalidValueException("Cannot sort across a to-many relationship: " + path.getFieldPath());
}
returnMap.put(path, order);
}
return returnMap;
}
use of com.yahoo.elide.core.Path in project elide by yahoo.
the class DefaultQueryValidator method validateHavingClause.
@Override
public void validateHavingClause(Query query) {
FilterExpression havingClause = query.getHavingFilter();
if (havingClause == null) {
return;
}
havingClause.accept(new PredicateExtractionVisitor()).forEach(predicate -> {
Path path = predicate.getPath();
if (path.getPathElements().size() > 1) {
throw new InvalidOperationException("Relationship traversal not supported for analytic queries.");
}
validatePredicate(query, predicate);
extractFilterProjections(query, havingClause).stream().forEach(projection -> {
Predicate<ColumnProjection> filterByNameAndArgs = (column) -> (column.getAlias().equals(projection.getAlias()) || column.getName().equals(projection.getName())) && column.getArguments().equals(projection.getArguments());
// Query by (alias or name) and arguments. The filter may or may not be using the alias.
if (query.getColumnProjection(filterByNameAndArgs) == null) {
Predicate<ColumnProjection> filterByName = (column) -> (column.getAlias().equals(projection.getAlias()) || column.getName().equals(projection.getName()));
// The column wasn't projected at all.
if (query.getColumnProjection(filterByName) == null) {
throw new InvalidOperationException(String.format("Post aggregation filtering on '%s' requires the field to be projected in the response", projection.getAlias()));
// The column was projected but arguments didn't match.
} else {
throw new InvalidOperationException(String.format("Post aggregation filtering on '%s' requires the field to be projected " + "in the response with matching arguments", projection.getAlias()));
}
}
});
});
}
Aggregations