Releases: bearsunday/BEAR.Package
Releases · bearsunday/BEAR.Package
1.3.0
- [ADD] PSR logger support #240
- [UPDATE] Error handling #241
- [ADD] Support
@Curies
(Compact URIs) annotation for API docs #239 - [ADD] Support
@ReturnCreatedResource
annotation for201
Created response andLocation
header #239
PSR Logger
Monolog logger installed in default.
binding
protected function configure()
{
$this->bind(LoggerInterface::class)->toProvider(MonologProviver::class)->in(Scope::SINGLETON);
}
You can change in logger in your ProdModule
. The default logger provider is like this.
logger Provider
class ProdMonologProviver implements ProviderInterface
{
/**
* @var AbstractAppMeta
*/
private $appMeta;
/**
* @param AbstractAppMeta $appMeta
*/
public function __construct(AbstractAppMeta $appMeta)
{
$this->appMeta = $appMeta;
}
public function get()
{
return new Logger($this->appMeta->name, [new ErrorLogHandler]);
}
}
You may change to your own logger in your prod
module.
Error handler
Change binding ErrorInterface
and have your own error handling.
namespace BEAR\Package\Provide\Error;
final class ErrorHandler implements ErrorInterface
{
/**
* @var ResourceObject
*/
private $errorPage;
/**
* @var TransferInterface
*/
private $responder;
/**
* @var ErrorLogger
*/
private $logger;
/**
* @var ErrorPageFactoryInterface
*/
private $factory;
public function __construct(TransferInterface $responder, ErrorLogger $logger, ErrorPageFactoryInterface $factory)
{
$this->responder = $responder;
$this->logger = $logger;
$this->factory = $factory;
}
/**
* {@inheritdoc}
*/
public function handle(\Exception $e, Request $request)
{
$this->logger->__invoke($e, $request);
$this->errorPage = $this->factory->newInstance($e, $request);
return $this;
}
/**
* {@inheritdoc}
*/
public function transfer()
{
$this->responder->__invoke($this->errorPage, []);
}
}
Error Page
namespace BEAR\Package\Provide\Error;
use BEAR\Resource\ResourceObject;
use BEAR\Sunday\Extension\Router\RouterMatch;
final class ProdVndErrorPage extends ResourceObject
{
public function __construct(\Exception $e, RouterMatch $request)
{
$status = new Status($e);
$this->code = $status->code;
$this->headers = $this->getHeader($status->code);
$this->body = $this->getResponseBody($e, $request, $status);
}
public function toString()
{
$this->view = json_encode($this->body, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL;
}
/**
* @return array
*/
private function getHeader($code)
{
return ['content-type' => ($code >= 500) ? 'application/vnd.error+json' : 'application/json'];
}
/**
* @param \Exception $e
* @param RouterMatch $request
* @param Status $status
*
* @return array
*/
private function getResponseBody(\Exception $e, RouterMatch $request, Status $status)
{
unset($request);
$body = ['message' => $status->text];
if ($status->code >= 500) {
$body['logref'] = (string) new LogRef($e);
}
return $body;
}
}
application/vnd.error+json error
404 Not Found
content-type: application/vnd.error+json
{
"message": "Not Found",
"logref": "ff4dbc05",
"request": "get page://self/a404",
"exceptions": "BEAR\\Resource\\Exception\\ResourceNotFoundException(page://self/a404)",
"file": "/Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/AppAdapter.php(78)"
}
logref.{logref}.log
Wed, 31 May 2017 11:10:58 +0200
get app://self/ (cli-hal-api-app)
Exceptions:
BEAR\Resource\Exception\ResourceNotFoundException(app://self/index) in /Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/AppAdapter.php(78)
Ray\Compiler\Exception\Unbound(Polidog\Todo\Resource\App\Index-) in /Users/akihito/git/Polidog.Todo/vendor/ray/compiler/src/ScriptInjector.php(178)
Ray\Compiler\Exception\ClassNotFound(Polidog\Todo\Resource\App\Index-) in /Users/akihito/git/Polidog.Todo/vendor/ray/compiler/src/ScriptInjector.php(177)
Trace:
#0 BEAR\Resource\AppAdapter->getNotFound(Object(BEAR\Resource\Uri),Object(Ray\Compiler\Exception\Unbound),'Polidog\Todo\Resource\App\Index') in /Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/AppAdapter.php(61)
#1 BEAR\Resource\AppAdapter->get(Object(BEAR\Resource\Uri)) in /Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/Factory.php(52)
#2 BEAR\Resource\Factory->newInstance(Object(BEAR\Resource\Uri)) in /Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/Resource.php(97)
#3 BEAR\Resource\Resource->newInstance(Object(BEAR\Resource\Uri)) in /Users/akihito/git/Polidog.Todo/vendor/bear/resource/src/Resource.php(126)
#4 BEAR\Resource\Resource->uri(Object(BEAR\Resource\Uri)) in /Users/akihito/git/Polidog.Todo/bootstrap/bootstrap.php(20)
#5 require('/Users/akihito/git/Polidog.Todo/bootstrap/bootstrap.php') in /Users/akihito/git/Polidog.Todo/bootstrap/api.php(4)
PHP Variables:
Array
(
[GET] => Array
(
)
[POST] => Array
var/log/app.log
[2017-05-31 10:31:03] Polidog\Todo.DEBUG: req:"get app://self/" code:404 e:BEAR\Resource\Exception\ResourceNotFoundException(app://self/index) logref:d75761fe [] []
[2017-05-31 10:31:13] Polidog\Todo.DEBUG: req:"get app://self/" code:404 e:BEAR\Resource\Exception\ResourceNotFoundException(app://self/index) logref:d75761fe [] []
[2017-05-31 11:09:39] Polidog\Todo.DEBUG: req:"get app://self/" code:404 e:BEAR\Resource\Exception\ResourceNotFoundException(app://self/index) logref:d75761fe [] []
var/log/context.prod-hal-app.log
Polidog\Todo\Module\App Object
(
[router] => BEAR\Package\Provide\Router\CliRouter Object
(
[router:BEAR\Package\Provide\Router\CliRouter:private] => BEAR\Package\Provide\Router\WebRouter Object
(
[schemeHost:BEAR\Package\Provide\Router\WebRouter:private] => app://self
[httpMethodParams:BEAR\Package\Provide\Router\WebRouter:private] =>
// .. 1800 lines...
@Curies
annotation supported
"CURIE"s help providing links to resource documentation.
HAL gives you a reserved link relation 'curies' which you can use to hint at the location of resource documentation. See more at http://stateless.co/hal_specification.html
/**
* @Curies(name="pd", href="api.exmaple.com/docs/{rels}", template=true)
*/
class Index extends ResourceObject
{
public $body = [
'_links' => [
'pd:todo' => ['href' => '/todo'],
'pd:todos' => ['href' =>'/todos']
]
];
public function onGet()
{
return $this;
}
}
bootstrap/api.php get /get /
200 OK
content-type: application/hal+json
{
"_links": {
"self": {
"href": "/index"
},
"curies": [
{
"href": "api.exmaple.com/docs/{rels}",
"name": "pd",
"templated": true
}
],
"pd:todo": {
"href": {
"href": "/todo"
}
},
"pd:todos": {
"href": {
"href": "/todos"
}
}
}
}
@ReturnCreatedResource
@ReturnCreatedResource
return newly created resource body specified with Location
header. . It worked only 201
response and Location
URI is valid.
/**
* @ReturnCreatedResource
*/
public function onPost(string $title, NowInterface $now = null) : ResourceObject
{
$value = [
'title' => $title,
'status' => self::INCOMPLETE,
'created' => (string) $now,
'updated' => (string) $now,
];
$this->pdo->perform($this->query['todo_insert'], $value);
$id = $this->pdo->lastInsertId();
$this->code = StatusCode::CREATED;
$this->headers[ResponseHeader::LOCATION] = "/todo?id={$id}";
return $this;
}
201 Created
Location: /todo?id=2
{
"todo": {
"id": "2",
"title": "run",
"status": "1",
"created": "2017-05-31 12:13:59",
"updated": "2017-05-31 12:13:59",
"status_name": "Complete"
},
"_links": {
"self": {
"href": "/todo?id=2"
}
}
}