Skip to content

Commit

Permalink
Replace Constraint with per-entity API based on AgEntityOverlay #493
Browse files Browse the repository at this point in the history
.. renaming access rules to filters for symmetry with #501 and #502
  • Loading branch information
andrus committed Nov 26, 2021
1 parent 2920ec0 commit 0b24ee7
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import io.agrest.Ag;
import io.agrest.DataResponse;
import io.agrest.access.PropertyAccessBuilder;
import io.agrest.filter.PropertyFilteringRulesBuilder;
import io.agrest.cayenne.cayenne.main.E2;
import io.agrest.cayenne.cayenne.main.E3;
import io.agrest.cayenne.cayenne.main.E4;
Expand Down Expand Up @@ -75,7 +75,7 @@ public static class Resource {
public DataResponse<E4> getObjects_LimitAttributes(@Context UriInfo uriInfo) {

return Ag.select(E4.class, config).uri(uriInfo)
.propertyAccess(E4.class, r -> r.empty().id(true).property("cInt", true))
.propFilter(E4.class, r -> r.empty().id(true).property("cInt", true))
.get();
}

Expand All @@ -86,7 +86,7 @@ public DataResponse<E3> getE2_E3s_Constrained(@PathParam("id") int id, @Context
return Ag.select(E3.class, config)
.parent(E2.class, id, "e3s")
.uri(uriInfo)
.propertyAccess(E3.class, PropertyAccessBuilder::idOnly)
.propFilter(E3.class, PropertyFilteringRulesBuilder::idOnly)
.get();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ public static class Resource {
@Path("e3/constrained")
public DataResponse<E3> insertE3ReadConstrained(@Context UriInfo uriInfo, String requestBody) {
return Ag.create(E3.class, config).uri(uriInfo)
.propertyReadAccess(E3.class, b -> b.idOnly().property("name", true))
.readablePropFilter(E3.class, b -> b.idOnly().property("name", true))
.syncAndSelect(requestBody);
}

@POST
@Path("e3/w/constrained")
public DataResponse<E3> insertE3WriteConstrained(@Context UriInfo uriInfo, String requestBody) {
return Ag.create(E3.class, config).uri(uriInfo)
.propertyWriteAccess(E3.class, b -> b.idOnly().property("name", true))
.writeablePropFilter(E3.class, b -> b.idOnly().property("name", true))
.syncAndSelect(requestBody);
}

Expand All @@ -143,7 +143,7 @@ public SimpleResponse create_WriteConstrainedId(
String requestBody) {

return Ag.create(E8.class, config).uri(uriInfo).id(id)
.propertyWriteAccess(E8.class, b -> b.idOnly().property("name", true))
.writeablePropFilter(E8.class, b -> b.idOnly().property("name", true))
.sync(requestBody);
}

Expand All @@ -154,7 +154,7 @@ public SimpleResponse create_WriteConstrainedIdBlocked(
@Context UriInfo uriInfo,
String requestBody) {
return Ag.create(E8.class, config).uri(uriInfo).id(id)
.propertyWriteAccess(E8.class, b -> b.empty().property("name", true))
.writeablePropFilter(E8.class, b -> b.empty().property("name", true))
.sync(requestBody);
}
}
Expand Down
17 changes: 9 additions & 8 deletions agrest-engine/src/main/java/io/agrest/SelectBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.agrest.encoder.EntityEncoderFilter;
import io.agrest.meta.AgEntity;
import io.agrest.meta.AgEntityOverlay;
import io.agrest.access.PropertyAccessRules;
import io.agrest.filter.PropertyFilter;
import io.agrest.processor.Processor;
import io.agrest.processor.ProcessorOutcome;
import io.agrest.runtime.AgBuilder;
Expand Down Expand Up @@ -87,22 +87,23 @@ public interface SelectBuilder<T> {
* @param constraint an instance of Constraint function.
* @return this builder instance.
* @since 2.4
* @deprecated since 4.8 in favor of {@link #propertyAccess(Class, PropertyAccessRules)}.
* @deprecated since 4.8 in favor of {@link #propFilter(Class, PropertyFilter)}.
*/
@Deprecated
SelectBuilder<T> constraint(Constraint<T> constraint);

/**
* Applies provided property access rules to the current request, defining which attributes and relationships a
* client can see for the specified entity type. Can be called multiple times to add multiple rules for same entity
* or different entities. So the "entityType" parameter can match the root entity or can be any other entity in the
* model. This method is a shortcut for "entityOverlay(AgEntity.overlay(entityType).readAccess(accessRules))".
* Adds a {@link PropertyFilter} that define property access rules for the current request and a given entity. I.e.
* which entity attributes, relationships and ids a client is allowed to see. Can be called multiple times to add
* multiple rules for same entity or different entities. The "entityType" parameter can match the root entity or
* can be any other entity in the model. This method is a shortcut for
* <code>entityOverlay(AgEntity.overlay(entityType).readablePropFilter(filter))</code>
*
* @return this builder instance
* @since 4.8
*/
default <A> SelectBuilder<T> propertyAccess(Class<A> entityType, PropertyAccessRules rules) {
return entityOverlay(AgEntity.overlay(entityType).readAccess(rules));
default <A> SelectBuilder<T> propFilter(Class<A> entityType, PropertyFilter filter) {
return entityOverlay(AgEntity.overlay(entityType).readablePropFilter(filter));
}

/**
Expand Down
32 changes: 17 additions & 15 deletions agrest-engine/src/main/java/io/agrest/UpdateBuilder.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.agrest;

import io.agrest.access.PropertyAccessRules;
import io.agrest.filter.PropertyFilter;
import io.agrest.constraints.Constraint;
import io.agrest.meta.AgEntity;
import io.agrest.meta.AgEntityOverlay;
Expand Down Expand Up @@ -62,7 +62,7 @@ public interface UpdateBuilder<T> {
* @param constraint an instance of Constraint function.
* @return this builder instance.
* @since 2.4
* @deprecated since 4.8 in favor of {@link #propertyReadAccess(Class, PropertyAccessRules)}
* @deprecated since 4.8 in favor of {@link #readablePropFilter(Class, PropertyFilter)}
*/
@Deprecated
UpdateBuilder<T> readConstraint(Constraint<T> constraint);
Expand All @@ -73,35 +73,37 @@ public interface UpdateBuilder<T> {
*
* @param constraint an instance of Constraint function.
* @return this builder instance
* @deprecated since 4.8 in favor of {@link #propertyWriteAccess(Class, PropertyAccessRules)}
* @deprecated since 4.8 in favor of {@link #writeablePropFilter(Class, PropertyFilter)}
*/
@Deprecated
UpdateBuilder<T> writeConstraint(Constraint<T> constraint);

/**
* Applies provided property read access rules to the current request, defining which attributes and relationships a
* client can view for the specified entity type. Can be called multiple times to add multiple rules for same entity
* or different entities. So the "entityType" parameter can match the root entity or can be any other entity in the
* model. This method is a shortcut for "entityOverlay(AgEntity.overlay(entityType).readAccess(accessRules))".
* Adds a {@link PropertyFilter} that define property read access rules for the current request and a given entity.
* I.e. which entity attributes, relationships and ids a client is allowed to see. Can be called multiple times to add
* multiple rules for same entity or different entities. The "entityType" parameter can match the root entity or
* can be any other entity in the model. This method is a shortcut for
* <code>entityOverlay(AgEntity.overlay(entityType).readablePropFilter(filter))</code>
*
* @return this builder instance
* @since 4.8
*/
default <A> UpdateBuilder<T> propertyReadAccess(Class<A> entityType, PropertyAccessRules rules) {
return entityOverlay(AgEntity.overlay(entityType).readAccess(rules));
default <A> UpdateBuilder<T> readablePropFilter(Class<A> entityType, PropertyFilter rules) {
return entityOverlay(AgEntity.overlay(entityType).readablePropFilter(rules));
}

/**
* Applies provided property write access rules to the current request, defining which attributes and relationships a
* client can modify for the specified entity type. Can be called multiple times to add multiple rules for same entity
* or different entities. So the "entityType" parameter can match the root entity or can be any other entity in the
* model. This method is a shortcut for "entityOverlay(AgEntity.overlay(entityType).writeAccess(accessRules))".
* Adds a {@link PropertyFilter} that define property write access rules for the current request and a given entity.
* I.e. which entity attributes, relationships and ids a client is allowed to see. Can be called multiple times to add
* multiple rules for same entity or different entities. The "entityType" parameter can match the root entity or
* can be any other entity in the model. This method is a shortcut for
* <code>entityOverlay(AgEntity.overlay(entityType).writeablePropFilter(filter))</code>
*
* @return this builder instance
* @since 4.8
*/
default <A> UpdateBuilder<T> propertyWriteAccess(Class<A> entityType, PropertyAccessRules rules) {
return entityOverlay(AgEntity.overlay(entityType).writeAccess(rules));
default <A> UpdateBuilder<T> writeablePropFilter(Class<A> entityType, PropertyFilter rules) {
return entityOverlay(AgEntity.overlay(entityType).writablePropFilter(rules));
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.agrest.constraints;

import io.agrest.filter.PropertyFilter;
import io.agrest.meta.AgEntity;

import java.util.function.Function;
Expand All @@ -8,7 +9,7 @@
* Metadata constraint, essentially a function that creates {@link ConstrainedAgEntity} from {@link AgEntity}.
*
* @since 2.4
* @deprecated since 4.8 in favor of non-hierarchical per-entity {@link io.agrest.access.PropertyAccessRules} API
* @deprecated since 4.8 in favor of non-hierarchical per-entity {@link PropertyFilter} API
*/
@Deprecated
public interface Constraint<T> extends Function<AgEntity<T>, ConstrainedAgEntity<T>> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.agrest.constraints;

import io.agrest.PathConstants;
import io.agrest.filter.PropertyFilter;
import io.agrest.base.protocol.Exp;
import io.agrest.meta.AgEntity;
import io.agrest.meta.AgRelationship;
Expand All @@ -13,7 +14,7 @@
* in this class returns a new copy of Constraints, so it is safe to reuse instances including intermediate instances.
*
* @since 1.3
* @deprecated since 4.8 in favor of non-hierarchical per-entity {@link io.agrest.access.PropertyAccessRules} API
* @deprecated since 4.8 in favor of non-hierarchical per-entity {@link PropertyFilter} API
*/
@Deprecated
public class ConstraintsBuilder<T> implements Constraint<T> {
Expand Down
28 changes: 28 additions & 0 deletions agrest-engine/src/main/java/io/agrest/filter/PropertyFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.agrest.filter;

import java.util.Objects;

/**
* Encapsulates logic for creation of custom property access rules for a single entity. The logic is implemented in the
* {@link #apply(PropertyFilteringRulesBuilder)} method, where custom code invokes methods on an instance of
* {@link PropertyFilteringRulesBuilder} to define access rules.
*
* @see io.agrest.SelectBuilder#propFilter(Class, PropertyFilter)
* @since 4.7
*/
@FunctionalInterface
public interface PropertyFilter {

/**
* Configures property access rules via the provided {@link PropertyFilteringRulesBuilder}.
*/
void apply(PropertyFilteringRulesBuilder accessConfig);

default PropertyFilter andThen(PropertyFilter after) {
Objects.requireNonNull(after);
return pa -> {
apply(pa);
after.apply(pa);
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.agrest.access;
package io.agrest.filter;

import io.agrest.PathConstants;
import io.agrest.meta.AgEntity;
Expand All @@ -15,48 +15,48 @@
* A mutable builder of property access constraints for a single entity. Used to configure property access in
* {@link AgEntityOverlay}.
*
* @see PropertyAccessRules#apply(PropertyAccessBuilder)
* @see PropertyFilter#apply(PropertyFilteringRulesBuilder)
* @since 4.8
*/
public class PropertyAccessBuilder {
public class PropertyFilteringRulesBuilder {

// Since we can't resolve the access rules until we are given an entity,
// we must store them as "filter" operations in the order they were specified.
// During the resolution phase they will be applied in turn to the entity
// set of properties
private final List<Consumer<ExcludeBuilder<?>>> accessFilters;

public PropertyAccessBuilder() {
public PropertyFilteringRulesBuilder() {
this.accessFilters = new ArrayList<>();
}

/**
* Creates a rule to block access to all properties (ids, attributes, relationships) from the entity model.
*/
public PropertyAccessBuilder empty() {
public PropertyFilteringRulesBuilder empty() {
accessFilters.add(ExcludeBuilder::excludeEverything);
return this;
}

/**
* Creates a rule to block access to all properties, but allows access to id
*/
public PropertyAccessBuilder idOnly() {
public PropertyFilteringRulesBuilder idOnly() {
accessFilters.add(ExcludeBuilder::includeIdOnly);
return this;
}

/**
* Sets an access rule for the id property.
*/
public PropertyAccessBuilder id(boolean accessible) {
public PropertyFilteringRulesBuilder id(boolean accessible) {
return property(PathConstants.ID_PK_ATTRIBUTE, accessible);
}

/**
* Sets an access rule for all attribute properties.
*/
public PropertyAccessBuilder attributes(boolean accessible) {
public PropertyFilteringRulesBuilder attributes(boolean accessible) {
accessFilters.add(accessible
? ExcludeBuilder::includeAllAttributes
: ExcludeBuilder::excludeAllAttributes);
Expand All @@ -67,7 +67,7 @@ public PropertyAccessBuilder attributes(boolean accessible) {
/**
* Sets an access rule for all relationship properties.
*/
public PropertyAccessBuilder relationships(boolean accessible) {
public PropertyFilteringRulesBuilder relationships(boolean accessible) {
accessFilters.add(accessible
? ExcludeBuilder::includeAllRelationships
: ExcludeBuilder::excludeAllRelationships);
Expand All @@ -78,7 +78,7 @@ public PropertyAccessBuilder relationships(boolean accessible) {
/**
* Sets am access rule for a given named property, that can be an attribute, a relationship or an id.
*/
public PropertyAccessBuilder property(String name, boolean accessible) {
public PropertyFilteringRulesBuilder property(String name, boolean accessible) {
accessFilters.add(accessible
? b -> b.includeProperty(name)
: b -> b.excludeProperty(name));
Expand Down
Loading

0 comments on commit 0b24ee7

Please sign in to comment.