Skip to content
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

The builder clones itself before enrichment #2117

Merged

Conversation

vitalijr2
Copy link
Collaborator

@vitalijr2 vitalijr2 commented Jul 3, 2023

I have tried to resolve #1961: now when the builder makes a new *Feign it wraps own fields, on second call it wraps already wrapped fields. I replace the link to itself builder thisB and own fields by a clone and its fields.

Summary by CodeRabbit

Refactor:

  • Introduced a new method internalBuild() across various classes in the Feign library, improving code modularity and maintainability.
  • Enhanced the BaseBuilder class with cloning capabilities to enrich builder objects, enhancing code consistency and logic.
  • Updated test cases to reflect changes in the BaseBuilder class.

Hopping through the code, making it neat 🐇,
Refactoring here and there, isn't that sweet? 🍭
With every change, we're on a roll,
Making our codebase, beautifully whole. 🎉

@@ -186,30 +186,32 @@ public AsyncBuilder<C> invocationHandlerFactory(InvocationHandlerFactory invocat
}

public AsyncFeign<C> build() {
super.enrich();
AsyncBuilder<C> enrichedBuilder = super.enrich();
Copy link
Member

@velo velo Jul 3, 2023

Choose a reason for hiding this comment

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

My major concern is that in the future, someone will access a field directly, forget the enrichBuilder prefix

What if, we move the builder body to a new method, and the we invoke the "internalBuild()" in the enriched instance?

@coderabbitai
Copy link

coderabbitai bot commented Aug 21, 2023

Image description CodeRabbit

Walkthrough

The changes primarily focus on refactoring the build() method in various classes across multiple files. The build() method is renamed to internalBuild(), and a new build() method is introduced in the BaseBuilder class. This change enhances modularity and maintainability by centralizing common logic in the base class. Additionally, the BaseBuilder class now implements the Cloneable interface, allowing for more flexible object handling.

Changes

File Summary
core/src/main/java/feign/AsyncFeign.java
core/src/main/java/feign/BaseBuilder.java
core/src/main/java/feign/Feign.java
kotlin/src/main/java/feign/kotlin/CoroutineFeign.java
Refactored the build() method into internalBuild(). Introduced a new build() method in the BaseBuilder class that calls enrich() and internalBuild(). Added type parameters and implemented the Cloneable interface in BaseBuilder.
core/src/test/java/feign/BaseBuilderTest.java Updated the test method to reflect changes in the BaseBuilder class. Added an assertion to check that the original and enriched builder objects are not the same.
hystrix/src/main/java/feign/hystrix/HystrixFeign.java Renamed the build() method to internalBuild(). Adjusted import statements.
reactive/src/main/java/feign/reactive/ReactiveFeign.java
reactive/src/main/java/feign/reactive/ReactorFeign.java
reactive/src/main/java/feign/reactive/RxJavaFeign.java
Renamed the build() method to internalBuild(). In ReactiveFeign, added a check to ensure the contract instance is of type ReactiveDelegatingContract.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Actionable comments generated: 1

Commits Files that changed from the base of the PR and between b46ad26 and 87ed0af commits.
Files selected for processing (6)
  • core/src/main/java/feign/AsyncFeign.java (2 hunks)
  • core/src/main/java/feign/BaseBuilder.java (3 hunks)
  • core/src/main/java/feign/Feign.java (3 hunks)
  • core/src/test/java/feign/BaseBuilderTest.java (3 hunks)
  • hystrix/src/main/java/feign/hystrix/HystrixFeign.java (3 hunks)
  • kotlin/src/main/java/feign/kotlin/CoroutineFeign.java (2 hunks)
LGTM comments (Suppressed): 4
core/src/test/java/feign/BaseBuilderTest.java (3)
  • 17-17: The import org.junit.Assert.assertNotSame has been added. This is used to assert that two objects do not refer to the same object, which is useful for testing the cloning functionality in this context.

  • 34-37: The method signature of test() has been updated to take a BaseBuilder<?, ?> instead of BaseBuilder<?>. This change reflects the update in the BaseBuilder class where it now takes two type parameters. The enriched variable is now assigned the result of calling addCapability(mockingCapability).enrich() on the builder, which is expected due to the changes in the BaseBuilder class.

  • 50-50: An assertion has been added to verify that the original builder and the enriched builder are not the same object. This confirms that the enrich() method creates a clone of the builder before enrichment, as intended by the changes in the BaseBuilder class.

core/src/main/java/feign/BaseBuilder.java (1)
  • 32-32: The class now implements the Cloneable interface. This is a good practice when you want to create a copy of an object without affecting the original one, which seems to be the case here as per the PR summary. However, it's important to note that the clone() method in Java does a shallow copy of the object. If your class has complex objects (i.e., objects that contain other objects), you might need to override the clone() method and implement deep copying.
Additional comments (Suppressed): 11
kotlin/src/main/java/feign/kotlin/CoroutineFeign.java (2)
  • 115-116: The class CoroutineBuilder now extends BaseBuilder with two type parameters instead of one. Ensure that all instances of CoroutineBuilder throughout the codebase have been updated to match this new signature.

  • 160-162: The build() method has been replaced by internalBuild(). This change is consistent with the changes described in the PR summary. However, ensure that all calls to CoroutineBuilder.build() have been updated to CoroutineBuilder.internalBuild().

hystrix/src/main/java/feign/hystrix/HystrixFeign.java (3)
  • 16-19: The import statements have been rearranged. Ensure that this change does not affect the functionality of the code and is consistent with the project's coding style guide.

  • 30-32: The import statements have been moved. This should not affect the functionality of the code, but ensure it aligns with the project's coding style guide.

  • 136-138: The build() method has been renamed to internalBuild(). Make sure all calls to this method throughout the codebase have been updated to match the new method name.

core/src/main/java/feign/Feign.java (3)
  • 16-21: The import statements have been rearranged. Ensure that this change doesn't violate the project's code style guide. If there is no specific rule about the order of import statements, this change should be fine.

  • 95-95: The Builder class now extends BaseBuilder<Builder, Feign>. This change seems to be part of a larger refactoring effort to make the builder pattern more robust and flexible. Make sure all references to the Builder class in the codebase are updated accordingly.

  • 204-205: The build() method has been replaced with internalBuild(). This change is consistent with the PR summary, which mentions that the logic previously in the build() method is now in internalBuild(). However, ensure that all calls to build() throughout the codebase have been updated to match the new method name.

core/src/main/java/feign/AsyncFeign.java (2)
  • 68-68: The AsyncBuilder class now extends BaseBuilder with two type parameters instead of one. This change is consistent with the changes described in the PR summary and should not cause any issues as long as all references to AsyncBuilder in the codebase are updated accordingly.

  • 193-194: The build() method has been replaced with internalBuild(), which is called by the new build() method in BaseBuilder. The logic inside internalBuild() remains the same as the old build() method, except that it no longer calls enrich(). This is because enrich() is now called on a cloned instance of the builder in the new build() method in BaseBuilder. This change is consistent with the PR summary and should not cause any issues as long as all calls to build() in the codebase are updated to reflect this change.

core/src/main/java/feign/BaseBuilder.java (1)
  • 286-290: The build() method now calls enrich() on a cloned instance of the builder, followed by internalBuild(). This is a good design choice as it ensures that the original builder remains unmodified when build() is called. However, make sure that all subclasses of BaseBuilder have implemented the internalBuild() method, as it's now abstract and must be provided by any concrete subclass.

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Tag the bot with @coderabbitai in any review comment to ask it questions. The bot will not respond unless explicitly tagged.
  • A review comment is a comment on a diff or a file and not a comment on the PR itself.
  • You can use the mouse to select multiple lines of code to provide exact context to the bot.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +233 to +267
@SuppressWarnings("unchecked")
B enrich() {
if (capabilities.isEmpty()) {
return thisB;
}

getFieldsToEnrich().forEach(field -> {
field.setAccessible(true);
try {
final Object originalValue = field.get(thisB);
final Object enriched;
if (originalValue instanceof List) {
Type ownerType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
enriched = ((List) originalValue).stream()
.map(value -> Capability.enrich(value, (Class<?>) ownerType, capabilities))
.collect(Collectors.toList());
} else {
enriched = Capability.enrich(originalValue, field.getType(), capabilities);
try {
B clone = (B) thisB.clone();

getFieldsToEnrich().forEach(field -> {
field.setAccessible(true);
try {
final Object originalValue = field.get(clone);
final Object enriched;
if (originalValue instanceof List) {
Type ownerType =
((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
enriched = ((List) originalValue).stream()
.map(value -> Capability.enrich(value, (Class<?>) ownerType, capabilities))
.collect(Collectors.toList());
} else {
enriched = Capability.enrich(originalValue, field.getType(), capabilities);
}
field.set(clone, enriched);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("Unable to enrich field " + field, e);
} finally {
field.setAccessible(false);
}
field.set(thisB, enriched);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException("Unable to enrich field " + field, e);
} finally {
field.setAccessible(false);
}
});
});

return thisB;
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
Copy link

Choose a reason for hiding this comment

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

Image description CodeRabbit

The enrich() method has been updated to clone the builder before enriching it. This is a good approach to avoid mutating the original builder. However, there are some potential issues with exception handling:

  1. The CloneNotSupportedException is being caught and rethrown as an AssertionError. This could be misleading because AssertionError is usually thrown to indicate that an assertion has failed. It would be better to wrap the CloneNotSupportedException in a custom unchecked exception that indicates a failure to clone the builder.

  2. The IllegalArgumentException and IllegalAccessException are being caught and rethrown as a RuntimeException with a message indicating a failure to enrich a field. This is fine, but it would be more informative to include the name of the field in the error message.

Here's how you can improve the exception handling:

- throw new RuntimeException("Unable to enrich field " + field, e);
+ throw new RuntimeException("Unable to enrich field " + field.getName(), e);

- throw new AssertionError(e);
+ throw new BuilderCloneException("Failed to clone the builder", e);

You'll need to define the BuilderCloneException class, which could simply extend RuntimeException.

dependabot bot and others added 3 commits August 21, 2023 20:21
…OpenFeign#2151)

Bumps [io.sundr:sundr-maven-plugin](https://github.com/sundrio/sundrio) from 0.100.2 to 0.100.3.
- [Commits](https://github.com/sundrio/sundrio/commits)

---
updated-dependencies:
- dependency-name: io.sundr:sundr-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…hub.com:radio-rogal/feign into feign-builder-wraps-over-already-wrapped-fields
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Actionable comments generated: 0

Commits Files that changed from the base of the PR and between 87ed0af and 2e09730 commits.
Files selected for processing (1)
  • hystrix/src/main/java/feign/hystrix/HystrixFeign.java (4 hunks)
Files skipped from review due to trivial changes (1)
  • hystrix/src/main/java/feign/hystrix/HystrixFeign.java

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Tag the bot with @coderabbitai in any review comment to ask it questions. The bot will not respond unless explicitly tagged.
  • A review comment is a comment on a diff or a file and not a comment on the PR itself.
  • You can use the mouse to select multiple lines of code to provide exact context to the bot.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Actionable comments generated: 0

Commits Files that changed from the base of the PR and between 2e09730 and 9981206 commits.
Files selected for processing (1)
  • reactive/src/main/java/feign/reactive/ReactiveFeign.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • reactive/src/main/java/feign/reactive/ReactiveFeign.java

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Tag the bot with @coderabbitai in any review comment to ask it questions. The bot will not respond unless explicitly tagged.
  • A review comment is a comment on a diff or a file and not a comment on the PR itself.
  • You can use the mouse to select multiple lines of code to provide exact context to the bot.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Actionable comments generated: 0

Commits Files that changed from the base of the PR and between 9981206 and 540370d commits.
Files selected for processing (2)
  • reactive/src/main/java/feign/reactive/ReactorFeign.java (2 hunks)
  • reactive/src/main/java/feign/reactive/RxJavaFeign.java (2 hunks)
LGTM comments (Suppressed): 1
reactive/src/main/java/feign/reactive/ReactorFeign.java (1)
  • 17-23: The import statements have been rearranged. While this doesn't affect the functionality of the code, it's a good practice to keep related imports grouped together for better readability and maintainability.
Additional comments (Suppressed): 3
reactive/src/main/java/feign/reactive/RxJavaFeign.java (2)
  • 16-23: The import statements have been rearranged. Ensure that this change does not violate any project-specific style guides or conventions.

  • 36-38: The build() method has been renamed to internalBuild(). This is consistent with the changes described in the PR summary. However, ensure that all calls to this method throughout the codebase have been updated to match the new method name.

-    public Feign build() {
+    public Feign internalBuild() {
reactive/src/main/java/feign/reactive/ReactorFeign.java (1)
  • 44-46: The internalBuild() method has been updated to call the parent class's internalBuild() method instead of build(). This change is consistent with the PR summary and seems to be part of the effort to prevent wrapping already wrapped fields. However, ensure that all calls to this function throughout the codebase have been updated to match the new signature.

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Tag the bot with @coderabbitai in any review comment to ask it questions. The bot will not respond unless explicitly tagged.
  • A review comment is a comment on a diff or a file and not a comment on the PR itself.
  • You can use the mouse to select multiple lines of code to provide exact context to the bot.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

@velo velo merged commit fc6daef into OpenFeign:master Aug 21, 2023
@vitalijr2
Copy link
Collaborator Author

Oh, AI rules world :)

@vitalijr2 vitalijr2 deleted the feign-builder-wraps-over-already-wrapped-fields branch August 24, 2023 20:19
velo added a commit that referenced this pull request Oct 7, 2024
* Enrichment of a clone

---------

Co-authored-by: Marvin Froeder <marvin.froeder@dovetailstudios.com>
velo added a commit that referenced this pull request Oct 8, 2024
* Enrichment of a clone

---------

Co-authored-by: Marvin Froeder <marvin.froeder@dovetailstudios.com>
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.

MicrometerCapability is applied multiple times when using a Feign.Builder to build multiple Feign instances
2 participants