Skip to content

Commit

Permalink
Initial matcher and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bakura10 committed Dec 1, 2013
1 parent 4da206a commit 772944c
Show file tree
Hide file tree
Showing 29 changed files with 1,432 additions and 34 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ Then, add the keys "ZfrRest" to your modules list in `application.config.php` fi
`zfr_rest.global.php.dist` into your `autoload` folder (don't forget to remove the .dist extension at the end!). For
more details about how to use ZfrRest, please follow the [quick start](/docs/quick-start/01-introduction.md).

## Current limitations

ZfrRest currently suffers from the following flaws:

* ZfrRest only support POST and PUT for single resource (you cannot bulk create or bulk update)
* ZfrRest only supports JSON output
* Cannot assemble URLs from the router
* Links are not yet supported (ZfrRest is currently for target for internal used APIs rather than exposed APIs)
* ManyToMany associations are not yet supported (ie. URI like http://example.com/countries/capitals)
68 changes: 68 additions & 0 deletions src/ZfrRest/Mvc/Controller/MethodHandler/DataHydrationTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrRest\Mvc\Controller\MethodHandler;

use Zend\Stdlib\Hydrator\HydratorPluginManager;
use ZfrRest\Mvc\Exception\RuntimeException;
use ZfrRest\Options\ControllerBehavioursOptions;
use ZfrRest\Resource\ResourceInterface;

/**
* This trait is responsible for hydrating object with valid data
*
* @author Michaël Gallego <mic.gallego@gmail.com>
* @licence MIT
*/
trait DataHydrationTrait
{
/**
* @var HydratorPluginManager
*/
protected $hydratorPluginManager;

/**
* Hydrate the object bound to the data
*
* @param ResourceInterface $resource
* @param array $data
* @return array|object
* @throws RuntimeException
*/
public function hydrateData(ResourceInterface $resource, array $data)
{
if (!$this->getControllerBehavioursOptions()->getAutoHydrate()) {
return $data;
}

if (!($hydratorName = $resource->getMetadata()->getHydratorName())) {
throw new RuntimeException('No hydrator name has been found in resource metadata');
}

$hydrator = $this->hydratorPluginManager->get($hydratorName);

return $hydrator->hydrate($data, $resource->getData());
}

/**
* Get the controller behaviour options
*
* @return ControllerBehavioursOptions
*/
abstract public function getControllerBehavioursOptions();
}
11 changes: 9 additions & 2 deletions src/ZfrRest/Mvc/Controller/MethodHandler/DataValidationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
namespace ZfrRest\Mvc\Controller\MethodHandler;

use Zend\InputFilter\InputFilterPluginManager;
use ZfrRest\Http\Exception\Client\BadRequestException;
use ZfrRest\Mvc\Exception\RuntimeException;
use ZfrRest\Options\ControllerBehavioursOptions;
use ZfrRest\Resource\ResourceInterface;
Expand All @@ -42,7 +43,8 @@ trait DataValidationTrait
* @param ResourceInterface $resource
* @param array $data
* @return array
* @throws RuntimeException If no input filter metadata has been found
* @throws RuntimeException If no input filter is bound to the resource
* @throws BadRequestException If validation fails
*/
public function validateData(ResourceInterface $resource, array $data)
{
Expand All @@ -59,13 +61,18 @@ public function validateData(ResourceInterface $resource, array $data)
$inputFilter->setData($data);

if (!$inputFilter->isValid()) {

throw new BadRequestException(
'Validation error',
$inputFilter->getMessages()
);
}

return $inputFilter->getValues();
}

/**
* Get the controller behaviour options
*
* @return ControllerBehavioursOptions
*/
abstract public function getControllerBehavioursOptions();
Expand Down
120 changes: 120 additions & 0 deletions src/ZfrRest/Mvc/Controller/MethodHandler/PostHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrRest\Mvc\Controller\MethodHandler;

use Zend\InputFilter\InputFilterPluginManager;
use Zend\Mvc\Controller\AbstractController;
use Zend\Stdlib\Hydrator\HydratorPluginManager;
use ZfrRest\Http\Exception\Client\MethodNotAllowedException;
use ZfrRest\Options\ControllerBehavioursOptions;
use ZfrRest\Resource\ResourceInterface;

/**
* Handler for the POST method verb
*
* The POST request allow the client to create a new resource
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5
* @author Michaël Gallego <mic.gallego@gmail.com>
* @licence MIT
*/
class PostHandler implements MethodHandlerInterface
{
/**
* Traits
*/
use DataValidationTrait;
use DataHydrationTrait;

/**
* @var ControllerBehavioursOptions
*/
protected $controllerBehaviourOptions;

/**
* Constructor
*
* @param ControllerBehavioursOptions $controllerBehavioursOptions
* @param InputFilterPluginManager $inputFilterPluginManager
* @param HydratorPluginManager $hydratorPluginManager
*/
public function __construct(
ControllerBehavioursOptions $controllerBehavioursOptions,
InputFilterPluginManager $inputFilterPluginManager,
HydratorPluginManager $hydratorPluginManager
) {
$this->controllerBehaviourOptions = $controllerBehavioursOptions;
$this->inputFilterPluginManager = $inputFilterPluginManager;
$this->hydratorPluginManager = $hydratorPluginManager;
}

/**
* Handler for POST method
*
* POST method is used to create a new resource. On successful creation, POST method should return a HTTP status
* 201, with a Location header containing the URL of the newly created resource. We are doing several things for the
* user automatically:
* - we validate post data with the input filter defined in metadata
* - we hydrate valid data
* - we pass the object to the post method of the controller
*
* As you can see, the post method have three arguments: the object that is inserted, the resource metadata and
* the resource itself (which is the Collection where the object is added)
*
* Note that if you have set "auto_validate" and/or "auto_hydrate" to false in ZfrRest config, those steps will
* do nothing
*
* {@inheritDoc}
* @throws MethodNotAllowedException
*/
public function handleMethod(AbstractController $controller, ResourceInterface $resource)
{
// If no post method is defined on the controller, then we cannot do anything
if (!method_exists($controller, 'post')) {
throw new MethodNotAllowedException();
}

$singleResource = $resource->getMetadata()->createResource();
$data = json_decode($controller->getResponse()->getContent(), true);

$data = $this->validateData($singleResource, $data);
$data = $this->hydrateData($singleResource, $data);

$result = $controller->post($data, $singleResource->getMetadata());

// Set the Location header with the URL of the newly created resource
if (is_object($result)) {
// @TODO: when we have a functional router, we should be able to automatically set the Location
// header with the URI of the newly created resource
$controller->getResponse()->setStatusCode(201);
}

return $result;
}

/**
* Get the controller behaviour options
*
* @return ControllerBehavioursOptions
*/
public function getControllerBehavioursOptions()
{
return $this->controllerBehaviourOptions;
}
}
105 changes: 105 additions & 0 deletions src/ZfrRest/Mvc/Controller/MethodHandler/PutHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace ZfrRest\Mvc\Controller\MethodHandler;

use Zend\InputFilter\InputFilterPluginManager;
use Zend\Mvc\Controller\AbstractController;
use Zend\Stdlib\Hydrator\HydratorPluginManager;
use ZfrRest\Http\Exception\Client\MethodNotAllowedException;
use ZfrRest\Options\ControllerBehavioursOptions;
use ZfrRest\Resource\ResourceInterface;

/**
* Handler for the PUT method verb
*
* The PUT request allow the client to modify an existing resource
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6
* @author Michaël Gallego <mic.gallego@gmail.com>
* @licence MIT
*/
class PutHandler implements MethodHandlerInterface
{
/**
* Traits
*/
use DataValidationTrait;
use DataHydrationTrait;

/**
* @var ControllerBehavioursOptions
*/
protected $controllerBehaviourOptions;

/**
* Constructor
*
* @param ControllerBehavioursOptions $controllerBehavioursOptions
* @param InputFilterPluginManager $inputFilterPluginManager
* @param HydratorPluginManager $hydratorPluginManager
*/
public function __construct(
ControllerBehavioursOptions $controllerBehavioursOptions,
InputFilterPluginManager $inputFilterPluginManager,
HydratorPluginManager $hydratorPluginManager
) {
$this->controllerBehaviourOptions = $controllerBehavioursOptions;
$this->inputFilterPluginManager = $inputFilterPluginManager;
$this->hydratorPluginManager = $hydratorPluginManager;
}

/**
* Handler for PUT method
*
* PUT method is used to update an existing resource. We are doing several things for the user automatically:
* - we validate post data with the input filter defined in metadata
* - we hydrate valid data to update existing resource
* - we pass the object to the put method of the controller
*
* Note that if you have set "auto_validate" and/or "auto_hydrate" to false in ZfrRest config, those steps will
* do nothing
*
* {@inheritDoc}
* @throws MethodNotAllowedException
*/
public function handleMethod(AbstractController $controller, ResourceInterface $resource)
{
// If no put method is defined on the controller, then we cannot do anything
if (!method_exists($controller, 'put')) {
throw new MethodNotAllowedException();
}

$data = json_decode($controller->getResponse()->getContent(), true);

$data = $this->validateData($resource, $data);
$data = $this->hydrateData($resource, $data);

return $controller->put($data, $resource->getMetadata());
}

/**
* Get the controller behaviour options
*
* @return ControllerBehavioursOptions
*/
public function getControllerBehavioursOptions()
{
return $this->controllerBehaviourOptions;
}
}
6 changes: 0 additions & 6 deletions src/ZfrRest/Resource/Metadata/Annotation/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ final class Collection implements AnnotationInterface
*/
public $controller;

/**
* @var string
*/
public $inputFilter;

/**
* @var string
*/
Expand All @@ -46,7 +41,6 @@ public function getValue()
{
return array(
'controller' => $this->controller,
'inputFilter' => $this->inputFilter,
'hydrator' => $this->hydrator
);
}
Expand Down
8 changes: 0 additions & 8 deletions src/ZfrRest/Resource/Metadata/CollectionResourceMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ public function getControllerName()
return $this->propertyMetadata['controller'];
}

/**
* {@inheritDoc}
*/
public function getInputFilterName()
{
return $this->propertyMetadata['inputFilter'];
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ interface CollectionResourceMetadataInterface
*/
public function getControllerName();

/**
* Get the input filter's FQCN to be used for this resource
*
* @return string|null
*/
public function getInputFilterName();

/**
* Get the hydrator's FQCN to be used for this resource
*
Expand Down
Loading

0 comments on commit 772944c

Please sign in to comment.