Skip to content

Commit

Permalink
FetchPlanBuilder.add() should accept property path together with Fetc…
Browse files Browse the repository at this point in the history
…hPlan and FetchMode jmix-framework#294
  • Loading branch information
knstvk committed Feb 21, 2022
1 parent cb16e74 commit b9d864a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 7 deletions.
68 changes: 61 additions & 7 deletions core/src/main/java/io/jmix/core/FetchPlanBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import io.jmix.core.metamodel.model.MetaClass;
import io.jmix.core.metamodel.model.MetaProperty;
import io.jmix.core.metamodel.model.MetaPropertyPath;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
Expand Down Expand Up @@ -165,29 +166,68 @@ public FetchPlanBuilder add(String property, Consumer<FetchPlanBuilder> consumer

/**
* Adds property with FetchPlan specified by {@code fetchPlanName}.
* <p>
* For example:
* <pre>
* FetchPlan orderFP = fetchPlans.builder(Order.class)
* .addFetchPlan(FetchPlan.BASE)
* .add("orderLines", FetchPlan.BASE)
* .add("orderLines.product", FetchPlan.BASE)
* .build();
* </pre>
*
* @param property property name
* @param property name of immediate property or dot separated property path, e.g. "address.country.name"
* @throws FetchPlanNotFoundException if specified by {@code fetchPlanName} FetchPlan not found for entity determined by {@code property}
* @throws RuntimeException if FetchPlan has been already built
*/
public FetchPlanBuilder add(String property, String fetchPlanName) {
checkState();
properties.add(property);
FetchPlan fetchPlan = fetchPlanRepository.getFetchPlan(metaClass.getProperty(property).getRange().asClass(), fetchPlanName);
fetchPlans.put(property, fetchPlan);

add(property);

FetchPlanBuilder targetBuilder = this;
MetaPropertyPath propertyPath = getMetaPropertyPath(property);
for (MetaProperty metaProperty : propertyPath.getMetaProperties()) {
if (metaProperty.getRange().isClass()) {
targetBuilder = getNestedPropertyBuilder(targetBuilder, metaProperty.getName());
}
}
FetchPlanBuilder propBuilder = targetBuilder;
propBuilder.addFetchPlan(fetchPlanName);

return this;
}

/**
* Adds property with FetchPlan specified by {@code fetchPlanName}.
* Adds property with FetchPlan specified by {@code fetchPlanName} and a specific fetch mode.
* <p>
* For example:
* <pre>
* FetchPlan orderFP = fetchPlans.builder(Order.class)
* .addFetchPlan(FetchPlan.BASE)
* .add("orderLines", FetchPlan.BASE, FetchMode.UNDEFINED)
* .add("orderLines.product", FetchPlan.BASE, FetchMode.UNDEFINED)
* .build();
* </pre>
*
* @param property property name
* @param property name of immediate property or dot separated property path, e.g. "address.country.name"
* @throws FetchPlanNotFoundException if specified by {@code fetchPlanName} FetchPlan not found for entity determined by {@code property}
* @throws RuntimeException if FetchPlan has been already built
*/
public FetchPlanBuilder add(String property, String fetchPlanName, FetchMode fetchMode) {
add(property, fetchPlanName);
fetchModes.put(property, fetchMode);

FetchPlanBuilder targetBuilder = this;
MetaPropertyPath propertyPath = getMetaPropertyPath(property);
if (propertyPath.getMetaProperties().length > 1) {
for (MetaProperty metaProperty : Arrays.copyOf(propertyPath.getMetaProperties(), propertyPath.getMetaProperties().length - 1)) {
if (metaProperty.getRange().isClass()) {
targetBuilder = getNestedPropertyBuilder(targetBuilder, metaProperty.getName());
}
}
}
targetBuilder.fetchModes.put(propertyPath.getMetaProperty().getName(), fetchMode);

return this;
}

Expand Down Expand Up @@ -420,5 +460,19 @@ protected void checkState() {
throw new RuntimeException("FetchPlanBuilder cannot be modified after build() invocation");
}

protected MetaPropertyPath getMetaPropertyPath(String property) {
MetaPropertyPath propertyPath = metaClass.getPropertyPath(property);
if (propertyPath == null) {
throw new IllegalArgumentException("Invalid property: " + property);
}
return propertyPath;
}

protected FetchPlanBuilder getNestedPropertyBuilder(FetchPlanBuilder builder, String name) {
FetchPlanBuilder propBuilder = builder.builders.get(name);
if (propBuilder == null) {
throw new IllegalStateException("Nested builder not found for property: " + name);
}
return propBuilder;
}
}
39 changes: 39 additions & 0 deletions core/src/test/java/fetch_plan_builder/FetchPlanBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package fetch_plan_builder;

import io.jmix.core.*;
import io.jmix.core.metamodel.model.MetaProperty;
import io.jmix.core.metamodel.model.MetadataObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -26,7 +28,9 @@
import test_support.app.TestAppConfiguration;
import test_support.app.entity.Owner;
import test_support.app.entity.Pet;
import test_support.app.entity.sales.Order;

import java.util.Collection;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -243,6 +247,41 @@ public void testMerge() {
assertFalse(merged.getProperty("owner").getFetchPlan().containsProperty("name"));
}

@Test
public void testFetchPlanForPropertyPath() {
FetchPlan orderFP = fetchPlans.builder(Order.class)
.addFetchPlan(FetchPlan.BASE)
.add("orderLines", FetchPlan.BASE)
.add("orderLines.product", FetchPlan.BASE)
.build();

FetchPlan orderLinesFP = orderFP.getProperty("orderLines").getFetchPlan();
assertTrue(containsBaseProperties(orderLinesFP));

FetchPlan productFP = orderLinesFP.getProperty("product").getFetchPlan();
assertTrue(containsBaseProperties(productFP));
}

@Test
public void testFetchPlanWithFetchModeForPropertyPath() {
FetchPlan orderFP = fetchPlans.builder(Order.class)
.addFetchPlan(FetchPlan.BASE)
.add("orderLines", FetchPlan.BASE, FetchMode.UNDEFINED)
.add("orderLines.product", FetchPlan.BASE, FetchMode.UNDEFINED)
.build();

assertEquals(FetchMode.UNDEFINED, orderFP.getProperty("orderLines").getFetchMode());
assertEquals(FetchMode.UNDEFINED, orderFP.getProperty("orderLines").getFetchPlan().getProperty("product").getFetchMode());
}

private boolean containsBaseProperties(FetchPlan fetchPlan) {
Collection<MetaProperty> properties = metadata.getClass(fetchPlan.getEntityClass()).getProperties();
return properties.stream()
.filter(metaProperty -> !metaProperty.getRange().isClass())
.map(MetadataObject::getName)
.allMatch(fetchPlan::containsProperty);
}

private boolean containsSystemProperties(FetchPlan view) {
List<String> systemProperties = metadataTools.getSystemProperties(metadata.getClass(view.getEntityClass()));
return systemProperties.stream().allMatch(view::containsProperty);
Expand Down

0 comments on commit b9d864a

Please sign in to comment.