Skip to content

Commit

Permalink
Add ModelHydrator
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-paterson committed Sep 7, 2018
1 parent eda8cc2 commit 9b1d71e
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/Common/Hydrator/ModelHydrator.php
@@ -0,0 +1,44 @@
<?php

namespace IBM\Watson\Common\Hydrator;

use IBM\Watson\Common\Exception\HydrationException;
use IBM\Watson\Common\Model\CreateableFromArray;
use IBM\Watson\Common\Util\ResponseParser;
use Psr\Http\Message\ResponseInterface;

class ModelHydrator implements HydratorInterface
{
use ResponseParser;

public function hydrate(ResponseInterface $response, $class = null)
{
if (null === $class) {
throw new HydrationException('The ModelHydrator requires a model class as the second parameter');
}

$body = $this->getBody($response);
if (!$this->isResponseJson($response)) {
$message = 'The ModelHydrator cannot hydrate a response with Content-Type: ';
throw new HydrationException($message . $response->getHeaderLine('Content-Type'));
}

$data = \json_decode($body, true);
if (\JSON_ERROR_NONE !== \json_last_error()) {
throw new HydrationException(sprintf(
'Error (%d) when trying to json_decode response: ',
json_last_error()
));
}

$ref = new \ReflectionClass($class);
if ($ref->implementsInterface(CreateableFromArray::class)) {
$model = \call_user_func($class.'::create', $data);
} else {
$model = new $class($data);
}

return $model;
}

}
9 changes: 9 additions & 0 deletions src/Common/Model/CreateableFromArray.php
@@ -0,0 +1,9 @@
<?php


namespace IBM\Watson\Common\Model;

interface CreateableFromArray
{
public static function create(array $data);
}
15 changes: 15 additions & 0 deletions src/Common/spec/Hydrator/ModelHydratorSpec.php
@@ -0,0 +1,15 @@
<?php

namespace spec\IBM\Watson\Common\Hydrator;

use IBM\Watson\Common\Hydrator\ModelHydrator;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ModelHydratorSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(ModelHydrator::class);
}
}
20 changes: 20 additions & 0 deletions src/Common/stubs/CreateableFromArrayModel.php
@@ -0,0 +1,20 @@
<?php

namespace IBM\Watson\Common\stubs;

use IBM\Watson\Common\Model\CreateableFromArray;

class CreateableFromArrayModel implements CreateableFromArray
{
private $param;

public function __construct($param)
{
$this->param = $param;
}

public static function create($data)
{
return new self($data['param']);
}
}
13 changes: 13 additions & 0 deletions src/Common/stubs/Model.php
@@ -0,0 +1,13 @@
<?php

namespace IBM\Watson\Common\stubs;

class Model
{
private $param;

public function __construct($param)
{
$this->param = $param;
}
}
82 changes: 82 additions & 0 deletions src/Common/tests/Hydrator/ModelHydratorTest.php
@@ -0,0 +1,82 @@
<?php


namespace IBM\Watson\Common\Hydrator\ArrayHydratorTest;


use IBM\Watson\Common\Hydrator\ModelHydrator;
use IBM\Watson\Common\stubs\CreateableFromArrayModel;
use IBM\Watson\Common\stubs\Model;
use PHPUnit\Framework\TestCase;
use Mockery as m;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

class ModelHydratorTest extends TestCase
{
private $response;
private $stream;
private $model;

public function setUp()
{
$this->response = m::mock(ResponseInterface::class)->makePartial();
$this->stream = m::mock(StreamInterface::class)->makePartial();
$this->model = m::mock(CreateableFromArrayModel::class)->makePartial();
}

public function testHydrate()
{
$this->stream->shouldReceive('__toString')->andReturn('{"param":"value"}');

$this->response->shouldReceive('getHeaderLine')->once()->andReturn('application/json');
$this->response->shouldReceive('getBody')->once()->andReturn($this->stream);

$hydrator = new ModelHydrator();

$content = $hydrator->hydrate($this->response, get_class($this->model));
$this->assertInstanceOf(CreateableFromArrayModel::class, $content);

$content = $hydrator->hydrate($this->response, Model::class);
$this->assertInstanceOf(Model::class, $content);
}

/**
* @expectedException \IBM\Watson\Common\Exception\HydrationException
* @expectedExceptionMessage The ModelHydrator requires a model class as the second parameter
*/
public function testNoModelSuppliedException()
{
$hydrator = new ModelHydrator();

$hydrator->hydrate($this->response);
}

/**
* @expectedException \IBM\Watson\Common\Exception\HydrationException
* @expectedExceptionMessage The ModelHydrator cannot hydrate a response with Content-Type: text/plain
*/
public function testNoneJsonExceptionIsThrown()
{
$this->stream->shouldReceive('__toString')->once()->andReturn('Plain text response');
$this->response->shouldReceive('getHeaderLine')->andReturn('text/plain');
$this->response->shouldReceive('getBody')->once()->andReturn($this->stream);

$hydrator = new ModelHydrator();
$hydrator->hydrate($this->response, get_class($this->model));
}

/**
* @expectedException \IBM\Watson\Common\Exception\HydrationException
*/
public function testInvalidJsonExceptionIsThrown()
{
$this->stream->shouldReceive('__toString')->once()->andReturn('{param:value}');

$this->response->shouldReceive('getHeaderLine')->once()->andReturn('application/json');
$this->response->shouldReceive('getBody')->once()->andReturn($this->stream);

$hydrator = new ModelHydrator();
$hydrator->hydrate($this->response, get_class($this->model));
}
}

0 comments on commit 9b1d71e

Please sign in to comment.