-
Notifications
You must be signed in to change notification settings - Fork 53
Add plugin client builder #158
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
Conversation
3edf370
to
3358fa6
Compare
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 a lot for the PR. I had a few comments above.
Updated with remarks from @sagikazarmark. I added the client in the constructor rather than the createClient to have a better "immutability" handling. But with that, the Or we could add a setter for that one, as having options is, as its name implies it, optionnal. I'm also not really convinced we should add a setter for the client though, I think the constructor should do the trick. Maybe adding a |
Just to be clear: I don't think in this case immutability is a good idea. It my not even be practical. For example: the builder could be called by various parts of a DIC. If it's immutable, essentially every call to it would be ineffective. So I think it's okay to have a mutable state in the builder. Also, what's the point of caching the client? Again, the builder might be called by a DIC in a terminal stage of the dep resolution process and then never used again. And as I said, the natural thing to expect from a function called createClient, accepting a client instance is to return one with my client inside, not an earlier cached one. I think it would be more confusing than useful. Creating multiple clients with different base clients can be a perfectly valid use case. So my second comment means that I think caching the client is not necessary, may even be harmful in some cases. |
src/PluginClientBuilder.php
Outdated
*/ | ||
public function __construct($client) | ||
{ | ||
if (!$client instanceof ClientInterface && !$client instanceof HttpClient && !$client instanceof HttpAsyncClient) { |
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 this targeting master (v2) is there a reason why to keep the check for both ClientInterface
and HttpClient
?
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 I understood @Nyholm, this is not specially for 2.0 (at least not blocking for it). I didn't check if there was a 1.x branch.
I'm not sure either if we should check for the ClientInterface
though...
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.
- Master branch is v2
- Yes there is a branch for v1...
- Master branch which is v2 has as min requirement httplug: ^2.0 . The client on this version extends
ClientInterface
I'm not sure either if we should check for the ClientInterface though...
- Therefore my suggestion was to check only ClientInterface...
:)
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.
Checking all three would make more sense for v1.
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.
I'll retarget if there's hope for a 1.x release then. @Nyholm @sagikazarmark ? If not, I'll remove the HttpClient
, as the async client is not in the psr-18.
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.
why aren't you sure about that ?
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.
ok sorry I haven't checked if plugin client accept an Async one.. it does..
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.
for easier upgrade paths, version 2 accepts both PSR-18 clients and the old Httplug client interface. and the async client which only exists in Httplug.
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.
So my suggestion is not accurate? What am I missing? check this
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.
I removed the HttpClient
, even though I'm also not sure due to @dbu's comment... I saw in the PR that introduced http-plug 2.0 (psr-18 compatible) that removing the compatibility for 1.0 was on purpose because it involved some magic.
So I should either target 1.x and thus support also the HttpClient
, or leave it as is and remove it in here. But, AFAIC, new features should only target 2.0 (master), so....
@Taluu I'm not against supporting this in 1.x as well per se, but 2.x is the new target of every feature now. |
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.
cool, thanks for the pull request!
i agree with mark that the builder should be mutable.
the plugin order is important. right now, we have addPlugin that appends at the end. what if we had prependPlugin and appendPlugin instead, to also add to the front?
we could do insertAfter and insertBefore additionally, but then the things using the builder would need to know about each other. or we could use a priorities - but that would collide with the name concept.
do we really need a remove method and implicit overwrite by setting the same name again? without remove, there would be no need for the name.
hm, another thing with overwriting: addPlugin adds at the end, except when you overwrite an existing name. if the name is already in the list, the overwritten plugin goes there.
I'll remove the immutability and the cache then. But for the plugin removal, it can be useful when we want to e.g remove a previous auth mechanism and replace it with another one. Unless the fact that a plugin in the bottom will somehow overwrite the previous one, I didn't check that case. Adding I'd rather add priorities actually. I'll try to work on that. |
Generally a builder serves one purpose. If you want for example create a base builder and use it later as the base for several other builders, you could add a Priorities sound like and interesting feature. It can especially be useful when the building does not happen at one place (eg. the aformentioned DIC example). IIRC symfony event dispatcher has some sort of priority support. Someone working on the HTTPlugBundle could jump in here as I imagine the bundle an similar solutions are first class citizens of this builder. |
PR updated. I removed the immutability, and added priorities for plugins instead of names. I had to remove the possibility to remove a plugin, as we now have no means to remove a specific plugin (as there's no name given). I chose to keep the fluent interface in the builder, as this is one of the few cases where it is legit. I'll update the first message with the examples. |
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.
great! i think with priorities its easier than with defined order and prepend/append insertBefore/insertAfter 👍
and i agree on the fluent interface for a builder.
i have some suggestions for documentation and commented on the options.
if the others agree with this design, we should have some tests to make sure priorities work as expected. and an entry in the documentation.
f9771be
to
021b245
Compare
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.
Love it!
It just miss a test (like testing priorities)
What's the status of this PR? |
IIRC, adding tests. I totally forgot about that one. 😂 |
Test added on the plugins priority check and the fact that the options should be passed onto the plugin client properly |
Builder in KnpLabs Github API allow remove plugin https://github.com/KnpLabs/php-github-api/blob/master/lib/Github/HttpClient/Builder.php |
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.
looks good to me. i think we can merge as is.
for the question of removing plugins: in general i am wary of such functionality, though the builder seems like the one place where it might be ok.
please help to understand the use cases: when would you need this? and how would you go about removing them? pass in the instance that should be removed? or remove by class with instanceof checks?
I'm also not in favor of "removing" stuff in a first pass. We could still consider it afterwards though. |
thanks for the contribution! |
What's in this PR?
This adds a plugin client builder, that allows to dynamically build a plugin client with plugins set on the go.
Why?
If you have some conditions on your plugin client (such as the auth can vary, some more headers can be dynamically added, ... like in the github's api from knp or my behat extension for http calls), it can be useful to not have a static plugin client with its plugin list, but to be able to add / remove them when needed.
As mentionned in the two repositories ([https://github.com/KnpLabs/php-github-api/blob/2.10.1/lib/Github/Client.php#L314-L331](Github's Client for the github php api of knp, or my behat extension), we both had to implement our own plugin builder, so this shows there's a "demand" for that.
We could have used the PluginClientFactory, but the problem with it is that when creating the client, we need to provide all the plugins on the go, which is not (IMO) practical.
Example Usage
Checklist
To Do