-
Notifications
You must be signed in to change notification settings - Fork 13
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
Support contextprovider as a method parameter #101
Support contextprovider as a method parameter #101
Conversation
@@ -76,7 +85,7 @@ private Class<?> getClass(final Object proxy) { | |||
return interfaces[0]; | |||
} | |||
|
|||
private Object getConfig(final String key) { | |||
private Object getConfig(final String key, final List<ContextProvider> contextProviders) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is a list, I'm missing a test case for more than 1 provider to showcase what happens if there is a conflict in their selection (or how precedence on the providers works in case of matches/no matches).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for bringing this @bocytko, I would like to get some suggestions around this if we should limit the number of context provider to 1
or if there is a need to support multiple context providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the value of supporting multiple ones? (or not)
You used a list for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You used a list for some reason.
It was just a side effect of array of args
in the method Object handleInvocation(Object proxy, Method method, Object[] args)
to simplify logic.
I modified the code to select only the first context provider when there is more than one, as the current ContextProvider
is generic enough to support multiple variables. There is a warning message if there are more than one context provider used as method parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I would allow multiple context providers. If multiple contexts are required, it could be impractical to add them all to the same context provider. For conflicts, you have the options
- fail on getting config
- specify behavior (e.g. take first / last)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I modified the code to accept multiple context and throw an exception if there are multiple contexts containing the same context key.
This reverts commit a00b7d2.
Thanks for taking a look at this, it's certainly an interesting capability for the library to explore 🚀 Looking at the requirement from a bird's eye view, also taking the discussion of #101 (comment) into account, I wonder: why use In order to support passing context directly into the configuration method, what do you think of something like this:
In the @rameshmalla wdyt? |
The rationale behind using context provider is to take advantage of the flexibility of the interface, and capitalize on the pre-existing knowledge base of the context provider. Usage of annotated method arguments indeed looks like a promising option, having said that we have to assess how we could fit this into a scenario where we do not need spring support to use baigan-config. There are some clean-ups lined up where the baigan conventional way of using context provider (using springs ContextConfiguration) could be removed and use the current approach in this PR as the only way to pass on context to evaluate conditions. In my opinion, at this juncture, we can narrow down the scope by reusing the existing context provider. Subsequently, we can explore the viability of incorporating |
Hi @rameshmalla, thanks a lot for your initiative. While previously working on Baigan, I consciously left this whole topic of My overall view is that the whole concept of a |
I completely agree with you. Indeed, the concepts of On the documentation front, I took the initiative to create a wiki page for Therefore, with the wiki, the current pull request, and the upcoming cleanup pull request, we should have a reasonably clear understanding of |
@lukas-c-wilhelm @lukasniemeier-zalando @bocytko let me know if you need more details on this implementation. If it looks good, we can merge this PR and start the cleanup process of legacy code. |
@@ -11,6 +12,7 @@ public interface SomeConfiguration { | |||
SomeConfigObject someConfig(); | |||
String someValue(); | |||
Boolean isThisTrue(); | |||
Boolean toggleFlag(CustomContextProvider customContextProvider,CustomContextProvider secondProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered just passing a Context
object with a context name and value or directly a Map
? This could be varargs to pass arbitrary contexts and not having to pass any if none is desired. What is the added value of having the ContextProvider
?
It would basically put the ContextProvider::getContextParam
call to the user, but would also make them more flexible in how they want to generate the context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, and there is also an almost similar suggestion from @lukasniemeier-zalando here. The main reason for reusing ContextProvider
as it is flexible enough to support multiple contexts such as below
public class WebContextProvider implements ContextProvider {
private final Set<String> PARAMS = ImmutableSet.of("customer", "login_type");
@Override
public String getContextParam(final String name) {
if ("customer".equals(name)) {
return environment.getAllowedCustomer();
} else if ("customer".equals(name)) {
return environment.getType();
} else {
return null;
}
}
@Override
public Set<String> getProvidedContexts() {
return PARAMS;
}
}
and also the main reason is to keep the changes small.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, okay. I'm not fully sold on that point. For your example, other solutions would work equally well. However, I don't want to block this here as I agree with moving into a direction that simplifies the whole thing. If @lukasniemeier-zalando has no objections, I'm fine with this step.
Thanks for the change. Combined with the planned cleanup, it is definitely an improvement! Overall, I wonder how useful the context feature is now that we can have structured configs (e.g. for "equal", one could do this relatively easily with a |
I have some comments, but none of them are blockers. |
@@ -95,6 +106,16 @@ private Object getConfig(final String key) { | |||
context.put(param, provider.getContextParam(param)); | |||
} | |||
|
|||
if (!CollectionUtils.isEmpty(contextProviders)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we later remove the old way of providing ContextProvider
instances, are there cases where it becomes less convenient for the user to provide the context? E.g. are context providers as request-scoped beans supported today?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it will be a breaking change. However, based on simple github search, we've found that the usage of the context provider by our clients has been minimal due to its less-than-obvious implementation. Instead, clients are opting to add additional logic after fetching configuration values, a task that could have been easily handled by context providers.
contextProviders.forEach(contextProvider -> { | ||
contextProvider | ||
.getProvidedContexts() | ||
.forEach(contextParam -> { | ||
if(context.containsKey(contextParam)){ | ||
throw new RuntimeException("Cannot have more than one context provider for the same context key "+contextParam); | ||
} | ||
context.put(contextParam, contextProvider.getContextParam(contextParam)); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noting: if we used the reflection approach suggested by @lukasniemeier-zalando , this check could be done on context creation instead of at query time, making this more efficient and fail earlier.
After some discussions with the team and @rameshmalla we are concluding that we take in this change. We will support @rameshmalla to work on the planned follow-ups, clean-ups and iterations right away. Many thanks! |
👍 |
Closes #100