Skip to content

feat: spec for new BaseFilter#115

Open
scardanzan wants to merge 2 commits into
masterfrom
base-filter
Open

feat: spec for new BaseFilter#115
scardanzan wants to merge 2 commits into
masterfrom
base-filter

Conversation

@scardanzan

@scardanzan scardanzan commented May 14, 2026

Copy link
Copy Markdown
Member

Spec and implementation for BaseFilter

Summary by CodeRabbit

  • New Features
    • Introduced an annotation-driven BaseFilter abstraction with fluent sorting and pagination.
    • Added field-level filtering annotations for attribute mapping, range comparisons, and configurable null handling.
    • Expanded DAO query APIs to run list, single-result, and count queries using BaseFilter.
  • Deprecation / Migration
    • Marked the legacy QuerySpec/constraint-based filtering API as deprecated in favor of BaseFilter.
  • Tests
    • Added comprehensive JPA hook and processor coverage for the new filter model.

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR introduces a comprehensive annotation-driven JPA criteria filtering abstraction named BaseFilter to replace the imperative QuerySpec/Constraint model. It adds a new BaseFilter class with ordering and pagination, field-level metadata annotations (@Attribute, @From, @To, @WhenNull), an AttributePathResolver utility for path traversal, a BaseFilterJpaProcessor pipeline for criteria building, DAO integration with extensibility hooks, integration tests, and a full specification document. The legacy QuerySpec APIs are marked deprecated without removal for backward compatibility.

Changes

BaseFilter JPA Criteria Filtering Implementation

Layer / File(s) Summary
BaseFilter model and filter annotations
backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/BaseFilter.java, Attribute.java, From.java, To.java, WhenNull.java, backend-core-model/src/test/java/.../BaseFilterTest.java
BaseFilter abstract class with Order enum, insertion-ordered orders map, and pagination fields (firstResult, maxResult); chainable setters and Lombok @SuperBuilder with non-negative validation. Four runtime-retained field annotations: @Attribute (dotted-path mapping with manual flag), @From/@To (inclusive/exclusive range bounds), and @WhenNull (SKIP or IS_NULL policy). Tests verify state preservation, ordering insertion order, pagination validation, and builder independence.
DAO interface with BaseFilter overloads
backend-core-data/src/main/java/com/flowingcode/backendcore/dao/QueryDao.java
Introduces three new primary DAO methods on QueryDao: filter(BaseFilter) returning List<T>, filterWithSingleResult(BaseFilter) returning Optional<T>, and count(BaseFilter) returning long. Prior QuerySpec overloads retained as deprecated (since 1.2.0, forRemoval=false) with Javadoc pointing to BaseFilter variants.
JPA attribute path resolver
backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/AttributePathResolver.java
AttributePathResolver utility resolves dotted entity attribute paths (e.g., "a.b.c") via auto-joining of intermediate associations. Reuses existing joins matching attribute name and JoinType; enforces leaf type compatibility via asSubclass with primitive boxing; configurable join type defaults to INNER.
BaseFilterJpaProcessor criteria pipeline
backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/BaseFilterJpaProcessor.java
Converts BaseFilter instances to typed JPA queries: caches per-class annotated field metadata (avoiding repeated reflection), validates annotation consistency, builds equality/range/null-handling predicates, resolves attribute paths with auto-join, applies sort orders, invokes DAO hooks, and manages pagination. Supports both list and count query paths with identical predicate application.
DAO support integration and hooks
backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/ConversionJpaDaoSupport.java
Wires BaseFilterJpaProcessor into the DAO layer: filter, filterWithSingleResult, count methods delegate to processor and apply entity conversions. Adds extensibility hooks (customizePredicates for extra predicates, customizeCriteria for live criteria mutation) and field-reading helpers (getFilterFieldValue overloads) backed by cached reflection. Deprecates prior QuerySpec methods (since 1.2.0, forRemoval=false).
Integration tests for BaseFilter hooks
backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/BaseFilterDaoHookTest.java, JpaDaoSupportTest.java
Tests BaseFilter query execution: validates hook invocation on both filter() and count() paths; verifies predicate customization behavior; tests manual=true field handling via getFilterFieldValue; confirms validation rejection of invalid manual+range combinations; validates unknown-field rejection and live criteria mutation via customizeCriteria.
Legacy QuerySpec API deprecation
backend-core-model/src/main/java/com/flowingcode/backendcore/model/, backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/ConstraintTransformerJpaImpl.java
Marks entire QuerySpec legacy hierarchy as deprecated (since 1.2.0, forRemoval=false): QuerySpec, Constraint, ConstraintBuilder, ConstraintTransformer, ConstraintTransformerException, ConstraintTransformerJpaImpl, and all constraint implementations (AttributeConstraint, AttributeBetweenConstraint, AttributeLikeConstraint, etc.). Deprecation notices direct users to BaseFilter.
Feature specification document
specs/base-filter.md
Comprehensive specification: motivation and goals (declarative replacement for imperative QuerySpec), BaseFilter model with dual construction patterns, annotation semantics and interaction rules, DAO surface and processor pipeline definition, extensibility hooks, concrete PersonFilter example, deprecation strategy with shared path utility extraction, module placement, open questions (default sort, validation timing, field inheritance), and explicit out-of-scope items (operator-specific annotations, boolean composition, eventual QuerySpec removal).

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • FlowingCode/backend-core#108: Extends ConstraintTransformerJpaImpl disjunction/OR handling with LEFT-join logic; this PR deprecates that class while refactoring its path/join logic into a shared non-deprecated utility.

Suggested Reviewers

  • mlopezFC
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: introduction of a new BaseFilter specification and implementation across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch base-filter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Basic default implementation for the BaseFilters, extendable to enable creation of custom constraints.
Deprecating QuerySpec and all related methods
@sonarqubecloud

Copy link
Copy Markdown

@scardanzan scardanzan marked this pull request as ready for review June 23, 2026 19:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
backend-core-model/src/test/java/com/flowingcode/backendcore/model/filter/BaseFilterTest.java (1)

139-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expand toBuilder regression coverage to include orders isolation

The current test proves scalar independence only. Add an assertion path that mutates orders in the cloned builder so aliasing bugs on mutable fields are caught.

Suggested test update
 	`@Test`
 	void toBuilder_producesIndependentCopy() {
-		SampleFilter original = SampleFilter.builder().name("Ada").maxResult(50).build();
-		SampleFilter tweaked = original.toBuilder().maxResult(10).build();
+		SampleFilter original = SampleFilter.builder()
+				.name("Ada")
+				.addOrder("name")
+				.maxResult(50)
+				.build();
+		SampleFilter tweaked = original.toBuilder()
+				.addOrder("birthDate", BaseFilter.Order.ASC)
+				.maxResult(10)
+				.build();
 
 		assertEquals(50, original.getMaxResult());
 		assertEquals(10, tweaked.getMaxResult());
 		assertEquals("Ada", tweaked.getName());
+		assertIterableEquals(Arrays.asList("name"), original.getOrders().keySet());
+		assertIterableEquals(Arrays.asList("name", "birthDate"), tweaked.getOrders().keySet());
 		assertNotSame(original, tweaked);
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend-core-model/src/test/java/com/flowingcode/backendcore/model/filter/BaseFilterTest.java`
around lines 139 - 147, The test method `toBuilder_producesIndependentCopy`
currently only verifies independence of scalar fields like name and maxResult.
Add test assertions to verify that mutable collection fields like orders are
also independently copied and not aliased. Modify the test to create a
SampleFilter with initial orders, clone it using toBuilder(), mutate the orders
collection in the cloned builder, then assert that the original filter's orders
remain unchanged while the cloned filter's orders reflect the mutation. This
ensures proper deep copying of mutable fields and catches potential aliasing
bugs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/AttributePathResolver.java`:
- Around line 75-83: The resolve method accepts malformed attribute paths that
fail later with provider-specific exceptions instead of failing fast. Add
explicit validation for the attributePath parameter after the null check to
ensure the path is not blank, does not have leading or trailing dots, and does
not contain consecutive dots (like a..b). Throw an IllegalArgumentException with
a descriptive message if any of these conditions are detected, before proceeding
to split and process the path. This will catch invalid inputs deterministically
at the entry point of the resolve method.

In
`@backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/BaseFilterJpaProcessor.java`:
- Around line 107-113: The filterWithSingleResult method calls filter(filter)
which applies paging settings from the filter parameter, potentially truncating
results before the greater-than-one check occurs. This masks cases where
multiple matches actually exist. Refactor the filterWithSingleResult method to
create a dedicated query that bypasses the filter's firstResult and maxResult
paging settings and instead limits results to 2 rows for cardinality detection.
This ensures the method reliably detects when more than one match exists
regardless of any paging configuration in the original filter.

In
`@backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/BaseFilterDaoHookTest.java`:
- Around line 41-42: The EntityManagerFactory created in the setUp() method is
never closed, causing resource leaks. Add an `@AfterEach` import from
org.junit.jupiter.api and create a corresponding tearDown() or cleanup() method
annotated with `@AfterEach` that properly closes the EntityManagerFactory instance
after each test completes. This ensures resources are released and prevents
destabilization of longer test runs.

In
`@backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/BaseFilter.java`:
- Around line 148-153: The addOrder method in BaseFilter mutates the orders map
in place, which causes issues when the builder originates from toBuilder()
because the builder and the original filter instance share the same map
reference. To fix this, ensure that when the builder is initialized (in the
toBuilder() method or builder constructor), the orders map is defensively copied
into a new LinkedHashMap rather than sharing the reference. This way, when
addOrder is called on the builder, it modifies only the builder's copy and not
the original filter's orders.

---

Nitpick comments:
In
`@backend-core-model/src/test/java/com/flowingcode/backendcore/model/filter/BaseFilterTest.java`:
- Around line 139-147: The test method `toBuilder_producesIndependentCopy`
currently only verifies independence of scalar fields like name and maxResult.
Add test assertions to verify that mutable collection fields like orders are
also independently copied and not aliased. Modify the test to create a
SampleFilter with initial orders, clone it using toBuilder(), mutate the orders
collection in the cloned builder, then assert that the original filter's orders
remain unchanged while the cloned filter's orders reflect the mutation. This
ensures proper deep copying of mutable fields and catches potential aliasing
bugs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6799d05f-eeb9-49a1-9795-669ed50aab2f

📥 Commits

Reviewing files that changed from the base of the PR and between 4bcc761 and 7aa87b8.

📒 Files selected for processing (29)
  • backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/AttributePathResolver.java
  • backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/BaseFilterJpaProcessor.java
  • backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/ConstraintTransformerJpaImpl.java
  • backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/ConversionJpaDaoSupport.java
  • backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/BaseFilterDaoHookTest.java
  • backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/JpaDaoSupportTest.java
  • backend-core-data/src/main/java/com/flowingcode/backendcore/dao/QueryDao.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/Constraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/ConstraintBuilder.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/ConstraintTransformer.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/ConstraintTransformerException.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/QuerySpec.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeBetweenConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeILikeConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeInConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeLikeConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeNullConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeRelationalConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/DisjunctionConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/NegatedConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/RelationalConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/Attribute.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/BaseFilter.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/From.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/To.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/WhenNull.java
  • backend-core-model/src/test/java/com/flowingcode/backendcore/model/filter/BaseFilterTest.java
  • specs/base-filter.md
✅ Files skipped from review due to trivial changes (9)
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeInConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeConstraint.java
  • backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/JpaDaoSupportTest.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/DisjunctionConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/NegatedConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeRelationalConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/Constraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/constraints/AttributeBetweenConstraint.java
  • backend-core-model/src/main/java/com/flowingcode/backendcore/model/ConstraintBuilder.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • specs/base-filter.md

Comment on lines +75 to +83
public <V> Expression<V> resolve(String attributePath, Class<V> expectedType) {
Objects.requireNonNull(attributePath, "attributePath");
String[] path = attributePath.split("\\.");
String attributeName = path[path.length - 1];
String[] joinPath = Arrays.copyOf(path, path.length - 1);
Expression<?> expression = traverse(root, joinPath).get(attributeName);
boxed(expression.getJavaType()).asSubclass(expectedType);
return (Expression<V>) expression;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fail fast on malformed attribute paths.

resolve accepts malformed values (e.g. blank, leading/trailing dot, a..b) and then fails later with provider-specific exceptions. Add explicit validation here to return a deterministic IllegalArgumentException.

Proposed fix
 public <V> Expression<V> resolve(String attributePath, Class<V> expectedType) {
 		Objects.requireNonNull(attributePath, "attributePath");
+		if (attributePath.isBlank() || attributePath.startsWith(".")
+				|| attributePath.endsWith(".") || attributePath.contains("..")) {
+			throw new IllegalArgumentException("Invalid attributePath: " + attributePath);
+		}
 		String[] path = attributePath.split("\\.");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/AttributePathResolver.java`
around lines 75 - 83, The resolve method accepts malformed attribute paths that
fail later with provider-specific exceptions instead of failing fast. Add
explicit validation for the attributePath parameter after the null check to
ensure the path is not blank, does not have leading or trailing dots, and does
not contain consecutive dots (like a..b). Throw an IllegalArgumentException with
a descriptive message if any of these conditions are detected, before proceeding
to split and process the path. This will catch invalid inputs deterministically
at the entry point of the resolve method.

Comment on lines +107 to +113
Optional<T> filterWithSingleResult(BaseFilter filter) {
List<T> result = filter(filter);
if (result.size() > 1) {
throw new IllegalStateException("Current filter returned more than one result");
}
return result.stream().findFirst();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

filterWithSingleResult can return incorrect results when paging is set.

This method calls filter(filter), so firstResult/maxResult may truncate matches before the > 1 check. That violates the method contract and can hide multi-match cases. Build a dedicated query and cap it to 2 rows for cardinality detection, without applying filter paging.

Proposed fix
 Optional<T> filterWithSingleResult(BaseFilter filter) {
-		List<T> result = filter(filter);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<T> cq = cb.createQuery(persistentClass);
+		Root<T> root = cq.from(persistentClass);
+		cq.select(root);
+		applyPredicates(filter, cb, cq, root);
+		applyOrders(filter, cb, cq, root);
+		hooks.customizeCriteria(filter, cb, cq, root);
+		List<T> result = em.createQuery(cq).setMaxResults(2).getResultList();
 		if (result.size() > 1) {
 			throw new IllegalStateException("Current filter returned more than one result");
 		}
 		return result.stream().findFirst();
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend-core-data-impl/src/main/java/com/flowingcode/backendcore/dao/jpa/BaseFilterJpaProcessor.java`
around lines 107 - 113, The filterWithSingleResult method calls filter(filter)
which applies paging settings from the filter parameter, potentially truncating
results before the greater-than-one check occurs. This masks cases where
multiple matches actually exist. Refactor the filterWithSingleResult method to
create a dedicated query that bypasses the filter's firstResult and maxResult
paging settings and instead limits results to 2 rows for cardinality detection.
This ensures the method reliably detects when more than one match exists
regardless of any paging configuration in the original filter.

Comment on lines +41 to +42
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Close EntityManagerFactory after each test.

A new EntityManagerFactory is created in setUp() for every test but never closed. This can leak resources and destabilize longer CI runs.

Proposed fix
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@
 	private HookDao dao;
+	private EntityManagerFactory emf;
@@
 	`@BeforeEach`
 	void setUp() {
-		EntityManagerFactory emf = Persistence.createEntityManagerFactory("person");
+		emf = Persistence.createEntityManagerFactory("person");
 		EntityManager em = emf.createEntityManager();
@@
 		dao = new HookDao(emf);
 	}
+
+	`@AfterEach`
+	void tearDown() {
+		if (emf != null && emf.isOpen()) {
+			emf.close();
+		}
+	}

Also applies to: 103-117

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend-core-data-impl/src/test/java/com/flowingcode/backendcore/dao/jpa/BaseFilterDaoHookTest.java`
around lines 41 - 42, The EntityManagerFactory created in the setUp() method is
never closed, causing resource leaks. Add an `@AfterEach` import from
org.junit.jupiter.api and create a corresponding tearDown() or cleanup() method
annotated with `@AfterEach` that properly closes the EntityManagerFactory instance
after each test completes. This ensures resources are released and prevents
destabilization of longer test runs.

Comment on lines +148 to +153
public B addOrder(String attribute, Order direction) {
if (this.orders == null) {
this.orders = new LinkedHashMap<>();
}
this.orders.put(attribute, direction);
return self();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

toBuilder().addOrder(...) can mutate the original filter instance

On Line 149-Line 153, the builder mutates this.orders in place. When the builder comes from toBuilder(), this can share the same map reference as the original object, so adding an order in the builder can modify the original filter before build(). That breaks copy-independence expectations.

Proposed fix
 		public B addOrder(String attribute, Order direction) {
-			if (this.orders == null) {
-				this.orders = new LinkedHashMap<>();
-			}
+			this.orders = (this.orders == null)
+					? new LinkedHashMap<>()
+					: new LinkedHashMap<>(this.orders);
 			this.orders.put(attribute, direction);
 			return self();
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend-core-model/src/main/java/com/flowingcode/backendcore/model/filter/BaseFilter.java`
around lines 148 - 153, The addOrder method in BaseFilter mutates the orders map
in place, which causes issues when the builder originates from toBuilder()
because the builder and the original filter instance share the same map
reference. To fix this, ensure that when the builder is initialized (in the
toBuilder() method or builder constructor), the orders map is defensively copied
into a new LinkedHashMap rather than sharing the reference. This way, when
addOrder is called on the builder, it modifies only the builder's copy and not
the original filter's orders.

@javier-godoy javier-godoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR introduce breaking changes, but it targets a minor version (and the commit is not marked as breaking):

  • com.flowingcode.backendcore.dao.QueryDao.count(com.flowingcode.backendcore.model.filter.BaseFilter):METHOD_ADDED_TO_INTERFACE
  • com.flowingcode.backendcore.dao.QueryDao.filter(com.flowingcode.backendcore.model.filter.BaseFilter):METHOD_ADDED_TO_INTERFACE
  • com.flowingcode.backendcore.dao.QueryDao.filterWithSingleResult(com.flowingcode.backendcore.model.filter.BaseFilter):METHOD_ADDED_TO_INTERFACE

(you can run mvn verify for a report on source and binary incompatible changes)

Action required: please refactor as a non-breaking feature; or confirm that a breaking change is intended, mark the commit as a breaking change and increment major version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants