Skip to content

Commit

Permalink
Merge 82e8838 into 7a4f828
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexLisenkov committed Dec 7, 2020
2 parents 7a4f828 + 82e8838 commit 0fdb825
Show file tree
Hide file tree
Showing 14 changed files with 454 additions and 6 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
all:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system:
- ubuntu-latest
php-versions:
- '7.3'
- '7.4'
- '8.0'
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl
ini-values: post_max_size=256M, short_open_tag=On
coverage: xdebug
tools: phpunit:9

- name: Composer Install
run: composer install --no-progress

- name: Unit tests
run: ./vendor/bin/phpunit --coverage-clover ./clover.xml

- name: Upload coverage results to Coveralls
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_FLAG_NAME: ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
run: |
composer global require php-coveralls/php-coveralls
php-coveralls --coverage_clover=clover.xml --json_path=clover.json -v
9 changes: 4 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
composer.phar
/docker/
/var/
/vendor/

# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
.idea
composer.lock
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,70 @@
# uuid-argument-resolver
# UUID Argument Resolver Bundle
ramsey/uuid argument resolver bundle for Symfony

[![Coverage Status](https://coveralls.io/repos/github/AlexLisenkov/uuid-argument-resolver/badge.svg?branch=main)](https://coveralls.io/github/AlexLisenkov/uuid-argument-resolver?branch=main)
![CI](https://github.com/AlexLisenkov/uuid-argument-resolver/workflows/CI/badge.svg)
![Packagist Downloads](https://img.shields.io/packagist/dt/alexlisenkov/uuid-argument-resolver-bundle)
![PHP Versions](https://img.shields.io/badge/PHP-%5E7.3%20%7C%7C%20%5E8.0-blue)

## Install
```shell
composer require alexlisenkov/uuid-argument-resolver-bundle
```

## Usage
```php
namespace App\Controllers;

use Psr\Http\Message\ResponseInterface;
use Ramsey\Uuid\UuidInterface;

class ResourceController
/**
* @Route("/{uuid}", name="show_resource", methods="GET")
*/
public function show(UuidInterface $resourceUuid, ResourceRepository $resourceRepository): ResponseInterface
{
$resource = $resourceRepository->findOneByUuid($resourceUuid);

if ($resource === null) {
return new ResourceNotFoundResponse();
}

return new ResourceResponse($resource);
}
```

## Handling invalid uuid
By default, it will respond with `400 Bad Request` with body `Invalid UUID`. But you can configure this by creating a service.

### Custom response
Create a factory that creates a `Psr\Http\Message\ResponseInterface`.
```php
namespace App\Factory;

use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response;

class InvalidUuidResponseFactory
{
public static function create(): ResponseInterface
{
return new Response(400, [], 'Invalid UUID');
}
}
```
Override the `alexlisenkov.uuid_argument_resolver_bundle.uuid_invalid_response` service with your factory.
```yaml
alexlisenkov.uuid_argument_resolver_bundle.uuid_invalid_response:
class: '@Psr\Http\Message\ResponseInterface'
factory: [ 'App\Factory\InvalidUuidResponseFactory', create ]
```
Now an invalid UUID will return your response.

## Testing
```shell
composer test
```

## Contributing
Contributions are welcome.
34 changes: 34 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "alexlisenkov/uuid-argument-resolver-bundle",
"description": "ramsey/uuid argument resolver bundle for Symfony",
"version": "0.0.1",
"type": "symfony-bundle",
"require": {
"php": "^7.3 || ^8.0",
"ramsey/uuid": "^4.1",
"symfony/psr-http-message-bridge": "^2.0",
"nyholm/psr7": "^1.3"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"symfony/http-kernel": "^5.2",
"symfony/dependency-injection": "^5.2",
"symfony/config": "^5.2"
},
"license": "MIT",
"authors": [
{
"name": "Alex Lisenkov",
"email": "alex@create.nl"
}
],
"autoload": {
"psr-4": { "AlexLisenkov\\UuidArgumentResolverBundle\\": "src/" }
},
"autoload-dev": {
"psr-4": { "AlexLisenkov\\UuidArgumentResolverBundle\\Tests\\": "tests/" }
},
"config": {
"sort-packages": true
}
}
14 changes: 14 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
24 changes: 24 additions & 0 deletions src/DependencyInjection/UuidArgumentResolverExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
* @codeCoverageIgnore
*/
class UuidArgumentResolverExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../Resources/config')
);

$loader->load('services.yaml');
}
}
14 changes: 14 additions & 0 deletions src/InvalidUuidResponseFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle;

use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;

class InvalidUuidResponseFactory
{
public static function create(): ResponseInterface
{
return new Response(\Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST, [], 'Invalid UUID');
}
}
52 changes: 52 additions & 0 deletions src/Resolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle;

use Generator;
use Psr\Http\Message\ResponseInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

class Resolver implements ArgumentValueResolverInterface
{
/**
* @var HttpFoundationFactoryInterface
*/
private $httpFoundationFactory;
/**
* @var ResponseInterface
*/
private $invalidUuidResponse;

public function __construct(HttpFoundationFactoryInterface $httpFoundationFactory, ResponseInterface $invalidUuidResponse)
{
$this->httpFoundationFactory = $httpFoundationFactory;
$this->invalidUuidResponse = $invalidUuidResponse;
}

public function supports(Request $request, ArgumentMetadata $argument): bool
{
if (UuidInterface::class !== $argument->getType()) {
return false;
}

if (Uuid::isValid($request->attributes->get($argument->getName()))) {
return true;
}

$this->httpFoundationFactory
->createResponse($this->invalidUuidResponse)
->send();

return false;
}

public function resolve(Request $request, ArgumentMetadata $argument): Generator
{
yield Uuid::fromString($request->attributes->get($argument->getName()));
}
}
13 changes: 13 additions & 0 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
_defaults:
autowire: true

alexlisenkov.uuid_argument_resolver_bundle.uuid_invalid_response:
class: '@Psr\Http\Message\ResponseInterface'
factory: [ 'AlexLisenkov\UuidArgumentResolverBundle\InvalidUuidResponseFactory', create ]

AlexLisenkov\UuidArgumentResolverBundle\Resolver:
tags:
- { name: controller.argument_value_resolver, priority: 150 }
arguments:
$invalidUuidResponse: '@alexlisenkov.uuid_argument_resolver_bundle.uuid_invalid_response'
14 changes: 14 additions & 0 deletions src/UuidArgumentResolverBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle;

use AlexLisenkov\UuidArgumentResolverBundle\DependencyInjection\UuidArgumentResolverExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class UuidArgumentResolverBundle extends Bundle
{
public function getContainerExtension(): UuidArgumentResolverExtension
{
return new UuidArgumentResolverExtension();
}
}
34 changes: 34 additions & 0 deletions tests/Dummy/DummyHttpFoundationFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle\Tests\Dummy;

use Psr\Http\Message\ResponseInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;

class DummyHttpFoundationFactory extends HttpFoundationFactory
{
/**
* @var array
*/
private $responses;

public function __construct()
{
parent::__construct();

$this->responses = [];
}

public function createResponse(ResponseInterface $psrResponse, bool $streamed = false): DummyHttpResponse
{
$response = new DummyHttpResponse(parent::createResponse($psrResponse, $streamed), $psrResponse);
$this->responses[] = $response;

return $response;
}

public function getResponses(): array
{
return $this->responses;
}
}
50 changes: 50 additions & 0 deletions tests/Dummy/DummyHttpResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace AlexLisenkov\UuidArgumentResolverBundle\Tests\Dummy;

use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response;

class DummyHttpResponse extends Response
{
/**
* @var Response|null
*/
private $sent = null;
/**
* @var Response
*/
private $inner;
/**
* @var ResponseInterface
*/
private $psrResponse;

public function __construct(Response $inner, ResponseInterface $psrResponse)
{
$this->inner = $inner;
$this->psrResponse = $psrResponse;
}

public function send(): void
{
$this->sent = clone $this->inner;
}

public function getSent(): Response
{
return $this->sent;
}

public function getPsrResponse(): ResponseInterface
{
return $this->psrResponse;
}

public function __call($name, $arguments)
{
if (method_exists($this->inner, $name)) {
return parent::$name(...$arguments);
}
}
}
Loading

0 comments on commit 0fdb825

Please sign in to comment.