Skip to content

Commit

Permalink
Boom: rewritten ApiMiddlewares to more plug-in based conceptions, fin…
Browse files Browse the repository at this point in the history
…alize content negotiation, added debugging view, synchronize URL matching (trailing slashes), added basic readme
  • Loading branch information
f3l1x committed Jan 24, 2017
1 parent 4ca48b9 commit 330e1dd
Show file tree
Hide file tree
Showing 31 changed files with 931 additions and 165 deletions.
60 changes: 60 additions & 0 deletions .docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# API - Guidelines

## Content

- [Installation - how to register an extension](#installation)

## Installation

Simpliest way to register this API is via [Nette\DI\CompilerExtension](https://api.nette.org/2.4/Nette.DI.CompilerExtension.html).

```yaml
extensions:
api: Contributte\Api\Bridges\DI\ApiAnnotationsExtension
```

This is complex example of API configuration with middleware support.

```yaml
extensions:
api: Contributte\Api\Bridges\DI\ApiAnnotationsExtension
middlewares: Contributte\Middlewares\DI\NetteMiddlewareExtension

services:
# Middlewares
middleware.tracy: Contributte\Middlewares\Middleware\TracyMiddleware
middleware.basepath: Contributte\Middlewares\Middleware\AutoBasePathMiddleware
middleware.router: Contributte\Middlewares\Middleware\RouterMiddleware([
"^/api/{path:.+}": @middleware.api,
"^/{path:.*}": @middleware.presenter
])

# Case #1 (handle API request)[api/]
middleware.api:
class: Contributte\Api\Bridges\Middlewares\ApiDataMiddleware([
Contributte\Api\Bridges\Middlewares\ApiRouter("^/api/{api}{?format:\\.json|debug}"),
Contributte\Api\Bridges\Middlewares\ApiContentNegotiation([
Contributte\Api\Bridges\Middlewares\Negotiation\UrlNegotiator([
"json": Contributte\Api\Bridges\Middlewares\Negotiation\Transformer\JsonTransformer(),
"debug": Contributte\Api\Bridges\Tracy\Negotiation\Transformer\DebugTransformer(),
"*": Contributte\Api\Bridges\Middlewares\Negotiation\Transformer\JsonTransformer()
])
]),
Contributte\Api\Bridges\Middlewares\ApiEmitter()
])

# Case #2 (handle classic nette application)
middleware.presenter:
class: Contributte\Middlewares\Middleware\PresenterMiddleware
setup:
- setErrorPresenter(Error)
- setCatchExceptions(FALSE)

- {class: App\Middlewares\RootMiddleware, inject: true}

middlewares:
middlewares:
- @middleware.tracy
- @middleware.basepath
- @middleware.router
```
3 changes: 0 additions & 3 deletions .docs/index.md

This file was deleted.

48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# API

:boom: Powerfull API (`PSR-7`, `REST`, `Relay`, `Middleware`, `GraphQL`, `DataQL`) for [`Nette Framework`](https://github.com/nette/).
:boom: Powerful API (`PSR-7`, `REST`, `Relay`, `Middleware`, `GraphQL`, `DataQL`, `Annotations`) for [`Nette Framework`](https://github.com/nette/).

-----

Expand Down Expand Up @@ -28,11 +28,53 @@ composer require contributte/api

| State | Version | Branch | PHP |
|-------------|---------|----------|----------|
| development | `^0.1` | `master` | `>= 5.6` |
| development | `^0.2` | `master` | `>= 5.6` |

## Prolog

This really powerful annotation based API library is build on top of the PSR-7 standard. It reuses immutable `Request` & `Response` objects.
It's well known that in Nette applications are an `alfa` and an `omega` `Nette\Appliction\UI\Presenter`(s). We've took an idea from
Java (Spring Framework) and many others and together we've created Controller-based API.

Controllers are small parts which can be register to DIC. They have a few public annotated methods. If the method is matched by router, it obtained
`ApiRequest` and `ApiResponse`. Here's come the magic, these simple objects hold `PSR-7` - `Request` & `Response` instances.

Thats all. Take a look, it's really simple.

```php
use Contributte\Api\Annotation\Controller\Controller;
use Contributte\Api\Annotation\Controller\Method;
use Contributte\Api\Annotation\Controller\Path;
use Contributte\Api\Annotation\Controller\RootPath;
use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Http\Response\ApiResponse;

/**
* @Controller
* @RootPath("/users")
*/
final class UsersController
{

/**
* @Path("/")
* @Method("GET")
* @param ApiRequest $request
* @param ApiResponse $response
* @return ApiResponse
*/
public function index(ApiRequest $request, ApiResponse $response)
{
$response->getPsr7()->getBody()->write('Hello world!');

return $response;
}
}
```

## Documentation

@todo
- [Installation - how to register an extension](https://github.com/contributte/api/tree/master/.docs#installation)

-----

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
"doctrine/annotations": "^1.3.0",
"nette/reflection": "^2.4.0",
"nette/di": "^2.4.4",
"contributte/middlewares": "^0.1.1"
"contributte/middlewares": "^0.2.0"
},
"suggest": {
"nette/di": "to register Nette extensions [ApiAnnotationsExtension]",
"doctrine/annotations": "to load annotations [DoctrineAnnotationLoader]",
"nette/reflection": "for annotations [NetteAnnotationLoader]",
"tracy/tracy": "for debugging [DebugResponse]",
"tracy/tracy": "for tracy panel [ApiPanel]",
"contributte/middlewares": "for middlewares [ApiMiddleware]"
},
"autoload": {
Expand Down
2 changes: 1 addition & 1 deletion src/Bridges/DI/ApiAnnotationsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Contributte\Api\Bridges\DI\Annotation\DoctrineAnnotationLoader;
use Contributte\Api\Bridges\DI\Annotation\NetteAnnotationLoader;
use Contributte\Api\Bridges\Tracy\ApiPanel\ApiPanel;
use Contributte\Api\Bridges\Tracy\Panel\ApiPanel\ApiPanel;
use Contributte\Api\Dispatcher\ApiDispatcher;
use Contributte\Api\Dispatcher\IDispatcher;
use Contributte\Api\Router\ApiRouter;
Expand Down
78 changes: 0 additions & 78 deletions src/Bridges/Middleware/ApiMiddleware.php

This file was deleted.

80 changes: 80 additions & 0 deletions src/Bridges/Middlewares/ApiContentNegotiation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Contributte\Api\Bridges\Middlewares;

use Contributte\Api\Bridges\Middlewares\Negotiation\INegotiator;
use Contributte\Api\Exception\Logical\InvalidStateException;
use Contributte\Api\Http\Request\ApiRequest;
use Contributte\Api\Http\Response\ApiDataResponse;
use Contributte\Api\Http\Response\ApiResponse;

class ApiContentNegotiation implements IInvoker
{

/** @var INegotiator[] */
protected $negotiators = [];

/**
* @param INegotiator[] $negotiators
*/
public function __construct(array $negotiators)
{
$this->negotiators = $negotiators;
}

/**
* API - INVOKING **********************************************************
*/

/**
* @param ApiRequest $request
* @param ApiResponse|ApiDataResponse $response
* @param callable $next
* @return ApiResponse
*/
public function invoke(ApiRequest $request, ApiResponse $response, callable $next)
{
// Pass to next invoker
$response = $next($request, $response);

// If the response is not a appropriate type of or
// does not have a any data, return response immediately
if (!($response instanceof ApiDataResponse) ||
!$response->isEmpty()
) {
return $response;
}

// Otherwise, pass to negotiator
return $this->negotiate($request, $response);
}

/**
* HELPERS *****************************************************************
*/

/**
* @param ApiRequest $request
* @param ApiResponse $response
* @return ApiResponse
*/
protected function negotiate(ApiRequest $request, ApiResponse $response)
{
if (!$this->negotiators) {
throw new InvalidStateException('Please add at least one negotiator');
}

foreach ($this->negotiators as $negotiator) {
// Pass to negotiator and check return value
$negotiated = $negotiator->negotiate($request, $response);

// If it's not NULL, we have an ApiResponse
if ($negotiated !== NULL) {
return $negotiated;
}
}

return $response;
}

}
22 changes: 22 additions & 0 deletions src/Bridges/Middlewares/ApiDataMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Contributte\Api\Bridges\Middlewares;

use Contributte\Api\Http\Response\ApiDataResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class ApiDataMiddleware extends ApiMiddleware
{

/**
* @param ServerRequestInterface $psr7Request
* @param ResponseInterface $psr7Response
* @return ApiDataResponse
*/
protected function createApiResponse(ServerRequestInterface $psr7Request, ResponseInterface $psr7Response)
{
return (new ApiDataResponse())->withPsr7($psr7Response);
}

}
Loading

0 comments on commit 330e1dd

Please sign in to comment.