Skip to content

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

Merged
merged 2 commits into from
Oct 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- Add BaseUriPlugin that combines AddHostPlugin and AddPathPlugin

## 1.2.1 - 2016-07-26

Expand Down
64 changes: 64 additions & 0 deletions spec/Plugin/AddPathPluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use Http\Message\StreamFactory;
use Http\Message\UriFactory;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
use PhpSpec\ObjectBehavior;

class AddPathPluginSpec extends ObjectBehavior
{
function let(UriInterface $uri)
{
$this->beConstructedWith($uri);
}

function it_is_initializable(UriInterface $uri)
{
$uri->getPath()->shouldBeCalled()->willReturn('/api');
Copy link
Member

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.


$this->shouldHaveType('Http\Client\Common\Plugin\AddPathPlugin');
}

function it_is_a_plugin(UriInterface $uri)
{
$uri->getPath()->shouldBeCalled()->willReturn('/api');

$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_adds_path(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getPath()->shouldBeCalled()->willReturn('/api');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withPath('/api/users')->shouldBeCalled()->willReturn($uri);
$uri->getPath()->shouldBeCalled()->willReturn('/users');

$this->beConstructedWith($host);
$this->handleRequest($request, function () {}, function () {});
}

function it_throws_exception_on_trailing_slash(UriInterface $host)
{
$host->getPath()->shouldBeCalled()->willReturn('/api/');

$this->beConstructedWith($host);
$this->shouldThrow('\LogicException')->duringInstantiation();
}

function it_throws_exception_on_empty_path(UriInterface $host)
{
$host->getPath()->shouldBeCalled()->willReturn('');

$this->beConstructedWith($host);
$this->shouldThrow('\LogicException')->duringInstantiation();
}
}
102 changes: 102 additions & 0 deletions spec/Plugin/BaseUriPluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use Http\Message\StreamFactory;
use Http\Message\UriFactory;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
use PhpSpec\ObjectBehavior;

class BaseUriPluginSpec extends ObjectBehavior
{
function let(UriInterface $uri)
{
$this->beConstructedWith($uri);
}

function it_is_initializable(UriInterface $uri)
{
$uri->getHost()->shouldBeCalled()->willReturn('example.com');
Copy link
Member

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?

Copy link
Contributor Author

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. 😉

Copy link
Contributor Author

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.

Copy link
Member

@joelwurtz joelwurtz Oct 18, 2016

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)

$uri->getPath()->shouldBeCalled()->willReturn('/api');

$this->shouldHaveType('Http\Client\Common\Plugin\BaseUriPlugin');
}

function it_is_a_plugin(UriInterface $uri)
{
$uri->getHost()->shouldBeCalled()->willReturn('example.com');
$uri->getPath()->shouldBeCalled()->willReturn('/api');

$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_adds_domain_and_path(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getScheme()->shouldBeCalled()->willReturn('http://');
$host->getHost()->shouldBeCalled()->willReturn('example.com');
$host->getPort()->shouldBeCalled()->willReturn(8000);
$host->getPath()->shouldBeCalled()->willReturn('/api');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
$uri->withPort(8000)->shouldBeCalled()->willReturn($uri);
$uri->withPath('/api/users')->shouldBeCalled()->willReturn($uri);
$uri->getHost()->shouldBeCalled()->willReturn('');
$uri->getPath()->shouldBeCalled()->willReturn('/users');

$this->beConstructedWith($host);
$this->handleRequest($request, function () {}, function () {});
}

function it_adds_domain(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getScheme()->shouldBeCalled()->willReturn('http://');
$host->getHost()->shouldBeCalled()->willReturn('example.com');
$host->getPort()->shouldBeCalled()->willReturn(8000);
$host->getPath()->shouldBeCalled()->willReturn('/');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
$uri->withPort(8000)->shouldBeCalled()->willReturn($uri);
$uri->getHost()->shouldBeCalled()->willReturn('');

$this->beConstructedWith($host);
$this->handleRequest($request, function () {}, function () {});
}

function it_replaces_domain_and_adds_path(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getScheme()->shouldBeCalled()->willReturn('http://');
$host->getHost()->shouldBeCalled()->willReturn('example.com');
$host->getPort()->shouldBeCalled()->willReturn(8000);
$host->getPath()->shouldBeCalled()->willReturn('/api');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
$uri->withPort(8000)->shouldBeCalled()->willReturn($uri);
$uri->withPath('/api/users')->shouldBeCalled()->willReturn($uri);
$uri->getPath()->shouldBeCalled()->willReturn('/users');

$this->beConstructedWith($host, ['replace' => true]);
$this->handleRequest($request, function () {}, function () {});
}
Copy link
Member

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 :) )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

}
48 changes: 48 additions & 0 deletions src/Plugin/AddPathPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Http\Client\Common\Plugin;

use Http\Client\Common\Plugin;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;

/**
* Prepend a base path to the request URI. Useful for base API URLs like http://domain.com/api.
*
* @author Sullivan Senechal <[email protected]>
*/
final class AddPathPlugin implements Plugin
{
/**
* @var UriInterface
*/
private $uri;

/**
* @param UriInterface $uri
*/
public function __construct(UriInterface $uri)
{
if ($uri->getPath() === '') {
throw new \LogicException('URI path cannot be empty');
}

if (substr($uri->getPath(), -1) === '/') {
throw new \LogicException('URI path cannot end with a slash.');
}

$this->uri = $uri;
}

/**
* {@inheritdoc}
*/
public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$request = $request->withUri($request->getUri()
->withPath($this->uri->getPath().$request->getUri()->getPath())
);

return $next($request);
}
}
54 changes: 54 additions & 0 deletions src/Plugin/BaseUriPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Http\Client\Common\Plugin;

use Http\Client\Common\Plugin;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;

/**
* Combines the AddHostPlugin and AddPathPlugin.
*
* @author Sullivan Senechal <[email protected]>
*/
final class BaseUriPlugin implements Plugin
{
/**
* @var AddHostPlugin
*/
private $addHostPlugin;

/**
* @var AddPathPlugin|null
*/
private $addPathPlugin = null;

/**
* @param UriInterface $uri Has to contain a host name and cans have a path.
* @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions
*/
public function __construct(UriInterface $uri, array $hostConfig = [])
{
$this->addHostPlugin = new AddHostPlugin($uri, $hostConfig);

if (rtrim($uri->getPath(), '/')) {
$this->addPathPlugin = new AddPathPlugin($uri);
}
}

/**
* {@inheritdoc}
*/
public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$addHostNext = function (RequestInterface $request) use ($next, $first) {
return $this->addHostPlugin->handleRequest($request, $next, $first);
};

if ($this->addPathPlugin) {
return $this->addPathPlugin->handleRequest($request, $addHostNext, $first);
}

return $addHostNext($request);
}
}