-
Notifications
You must be signed in to change notification settings - Fork 53
Add AddPathPlugin and BaseUriPlugin #48
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
1781c76
to
3c94066
Compare
as far as i remember, @sagikazarmark had a good argument why this should be a separate plugin. i can't find the place we discussed this anymore. mark would you rembember? doing this like you do here would be a BC break-ish thing as people that now use a domain with path and rely on the path not being appended would see their applications stop working. and its a new feature, not an actual bugfix so i think we should not do a BC break in a minor version for this. for the content, question is whether we treat |
This one? php-http/plugins#42 (comment) |
exactly, thanks! to sum it up: separate plugin for path:
if we would mix it together, would we only add the path if the host is missing, or always even if the host exists? should we check if the path already starts with the configured path and if so, skip adding it again (probably not)? @soullivaneuh what is your opinion on the other thread? would you agree that we move this to a separate plugin? |
AddHostPlugin only sets the host, scheme and port, ommits the path at the moment. |
Yes we talk about this and say it must be in a separate plugin like an |
@dbu This is technicaly a BC break, and you know how I'm pernickety with this for Sonata. 😉 But I agree with your point of view about the goal of the plugin, I hesitated for this too. I'll rework it for a
@joelwurtz What do you mean by "using"? I don't see how expect rewrite the logic of both on this class. |
Something like this : class BaseUriPlugin
{
private $addHostPlugin;
private $addPathPlugin;
public function __construct($options = [])
{
// @TODO deal with options
$this->addHostPlugin = new AddHostPlugin($options);
$this->addPathPlugin = new AddPathPlugin($options);
}
public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$addHostNext = function (Request $request) use ($next, $first) {
return $this->addHostPlugin->handleRequest($request, $next, $first);
};
return $this->addPathPlugin->handleRequest($request, $addHostNext, $first);
}
} |
i like joel's proposal. the coupling of BaseUriPlugin will be a bit tight like this. Maybe use two option arguments, one for host and one for path? that leaks the abstraction but is clean and future compatible |
My |
a179c23
to
1b62dc0
Compare
Code updated, thanks for reviewing. |
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.
Need to be fixed (see my comments) otherwise LGTM
return $this->addPathPlugin->handleRequest($request, $addHostNext, $first); | ||
} | ||
|
||
return $addHostNext; |
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.
Should be return $addHostNext($request);
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.
Fixed.
|
||
$this->beConstructedWith($host, ['replace' => true]); | ||
$this->handleRequest($request, function () {}, function () {}); | ||
} |
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.
Can you add test when there is no path ? (with my previous comment it should have failed :) )
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.
Fixed.
1b62dc0
to
2aac11b
Compare
public function __construct(UriInterface $host) | ||
{ | ||
if ($host->getPath() === '') { | ||
throw new \LogicException('Host path can not be empty'); |
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.
cannot
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.
Fixed.
/** | ||
* @param UriInterface $host | ||
*/ | ||
public function __construct(UriInterface $host) |
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.
By the way, what is the advantage of passing an UriInterface
instance here instead of a plain string?
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 guess the advantage is that we have validation. otherwise we would need to discover the uri factory or use parse_url to get the path part and make sure we have a path part.
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.
Do we really? the only with we do with $host is to convert it to a string..
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, actually we don't need a host here at all. The user just has to pass a path (not a host) and I am not sure that actually needs any validation at all.
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.
no, we do $host->getPath()
once here to validate, then again where we use it to just get the path part. if we would accept any string, you could create this plugin with "foo bar baz" and we would try to change the request uri to that. or we start to parse urls in here (which might have been validated and parsed before being passed), which is what the Uri model would free us from.
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 agree with @dbu.
This also make an easier argument passing for BaseUriPlugin
.
After that, it's as you want. I can pass a string instead if you prefer.
/** | ||
* @param UriInterface $host | ||
*/ | ||
public function __construct(UriInterface $host) |
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.
Does this need to be a URIInterface? It will always be converted to a string.
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.
Discussed here: #48 (comment)
private $addHostPlugin; | ||
|
||
/** | ||
* @var AddPathPlugin |
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.
this is AddPathPlugin|null
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 do not think the docblock here is necessary at all. IDEs should be able to infer the type themselves (the same applies to the $addHostPlugin
property).
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.
we are using these blocks throughout the code, imho its consistent to keep it. with the |null and a text saying this is the plugin for adding the path and only set if there is a path to add at all, it would be valuable
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.
Fixed.
{ | ||
$this->addHostPlugin = new AddHostPlugin($host, $hostConfig); | ||
|
||
if ($host->getPath()) { |
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.
we should rtrim($path, '/') here. or throw an exception on http://domain.com/ (it makes no sense to prepend just / to a path)
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.
Fixed.
public function handleRequest(RequestInterface $request, callable $next, callable $first) | ||
{ | ||
$request = $request->withUri($request->getUri() | ||
->withPath($this->host->getPath().$request->getUri()->getPath()) |
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 about anything ending on /? notably http://domain.com/ but also http://domain.com/path/ - both would end up in double leading slash, which is very likely not what the user wanted.
this is a rather annoying edge case. i think we should somehow handle it (maybe validate and InvalidArgumentException) to avoid wtf moments (some servers will handle the example.com//something paths, others wont). if somebody actually wants to create double slashes, they can write their own plugin, its a rare edgecase.
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 will add a test for that and handle it.
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.
Fixed.
2aac11b
to
f7bfea9
Compare
Requested changes was made. Except if you want me to change something on https://github.com/php-http/client-common/pull/48/files/2aac11b11bfd025c9d6082e722a1408139ab36f8#diff-44d3f10fc0543e373abc05223f369e01, this PR should be LGTM. |
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.
went over this and think we should throw an exception rather than eat away offending / characters. and some minor codestyle requests.
@@ -8,7 +8,8 @@ | |||
- Fix Emulated Trait to use Http based promise which respect the HttpAsyncClient interface | |||
- Require Httplug 1.1 where we use HTTP specific promises. | |||
- RedirectPlugin: use the full URL instead of the URI to properly keep track of redirects | |||
|
|||
- Add AddPathPlugin for API URLs with base path | |||
- Combine AddHostPlugin and AddPathPlugin onto BaseUriPlugin |
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.
Lets say "Add BaseUriPlugin that combines ..."
*/ | ||
public function __construct(UriInterface $host) | ||
{ | ||
if (rtrim($host->getPath(), '/') === '') { |
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 think i would check '' or last character is '/' and throw an exception when it is. silently fixing things makes harder debugging. after doing example.com/ once or twice, people will learn to use example.com
public function handleRequest(RequestInterface $request, callable $next, callable $first) | ||
{ | ||
$request = $request->withUri($request->getUri() | ||
->withPath(str_replace('//', '/', $this->host->getPath().$request->getUri()->getPath())) |
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.
this feels odd. i would rtrim '/' again. if somebody puts double slashes elsewhere, they probably wanted that. but see above...
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 throw an exception like asked in #48 (comment), we don't need to rtrim anything IMHO.
/** | ||
* @var UriInterface | ||
*/ | ||
private $host; |
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.
lets call this $uri, instead of $host. also in the constructor argument.
* @param UriInterface $host | ||
* @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions | ||
*/ | ||
public function __construct(UriInterface $host, array $hostConfig = []) |
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.
s/$host/$uri/
private $addPathPlugin = null; | ||
|
||
/** | ||
* @param UriInterface $host |
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.
we should say that this must contain a host name and may contain a path that must not end on /
36c470d
to
de9bff6
Compare
@dbu All requested changes was made. |
LGTM |
use Psr\Http\Message\UriInterface; | ||
|
||
/** | ||
* Add base path to the request URI. Useful for base API URLs like http://domain.com/api. |
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.
Maybe be more clear here:
"Prepend a base path to..."
use Psr\Http\Message\UriInterface; | ||
|
||
/** | ||
* This plugin combines the host and path plugins. |
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.
Maybe:
This plugin combines the AddHostPlugin and AddPathPlugin.
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.
Nice work, thanks!
|
||
function it_is_initializable(UriInterface $uri) | ||
{ | ||
$uri->getPath()->shouldBeCalled()->willReturn('/api'); |
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 think this could be moved to the let method. That would simplify things.
} | ||
|
||
function it_throws_exception_on_empty_path( | ||
UriInterface $host |
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 multiline?
} | ||
|
||
function it_throws_exception_on_trailing_slash( | ||
UriInterface $host |
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 multiline?
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, a copy/paste with parameters removal... 😛
|
||
function it_is_initializable(UriInterface $uri) | ||
{ | ||
$uri->getHost()->shouldBeCalled()->willReturn('example.com'); |
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.
Same here: can these be moved to let?
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 look at this, I'm more familiar with PHPUnit than PHPSpec. 😉
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 tried this:
function let(UriInterface $uri)
{
$uri->getPath()->shouldBeCalled()->willReturn('/api');
$this->beConstructedWith($uri);
}
But got this error:
Http/Client/Common/Plugin/AddPathPlugin
47 ✘ throws exception on trailing slash
some predictions failed:
Double\UriInterface\P114:
No calls have been made that match:
Double\UriInterface\P114->getPath()
but expected at least one.
Http/Client/Common/Plugin/AddPathPlugin
56 ✘ throws exception on empty path
some predictions failed:
Double\UriInterface\P116:
No calls have been made that match:
Double\UriInterface\P116->getPath()
but expected at least one.
I don't think this can be refactored on the let
method because the return value changes on some cases.
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.
AFAIK, let will set default config, so you can set the default input in the let
method and override it where it needs (when there is an empty path by example)
de9bff6
to
2c2e86d
Compare
@Nyholm @sagikazarmark Requested changes made, except #48 (comment) for the moment. |
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.
Awesome. I'm happy with these changes.
I'll let Mark decide on the PHPSpec comments.
great! there is one thing left to do: the documentation pull request so that these plugins are documented as well. can you please do that? |
Sure! Shall I just add two files on this part? https://github.com/php-http/documentation/tree/master/plugins |
Yes, I've added an issue for that. See php-http/documentation#162 |
Documentation added: php-http/documentation#163 |
thanks @soullivaneuh ! |
Thank you @soullivaneuh |
Thank you too! Could we please have a new release for this feature? Thanks. |
Shouldn't that have become 1.4.0? And by the way, could you fix the link to the changelog for the 1.3.0 release? :) |
I agree, it's a new feature. It should be a new minor if you follow semver ofc. |
Well, it didn't seem to be that huge change, but you are right. |
Done |
Thank you! |
What's in this PR?
This bugfix permits to keep the base path of an URI on the
AddHost
plugin.Why?
With the following PHP code example:
The https://logs.domain.com/users URL will be called instead of /api/users.
The base path should be kept on this plugin.
Checklist