Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional problems #8

Merged
merged 16 commits into from
Jun 12, 2020
241 changes: 185 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
[![Installs](https://img.shields.io/packagist/dt/phpro/api-problem.svg)](https://packagist.org/packages/phpro/api-problem/stats)
[![Packagist](https://img.shields.io/packagist/v/phpro/api-problem.svg)](https://packagist.org/packages/phpro/api-problem)


# Api Problem

This package provides a [RFC7807](https://tools.ietf.org/html/rfc7807) Problem details implementation.
Expand All @@ -13,7 +12,6 @@ Since handling the exceptions is up to the framework, here is a list of known fr

- **Symfony** `^4.1`: [ApiProblemBundle](https://www.github.com/phpro/api-problem-bundle)


## Installation

```sh
Expand All @@ -34,14 +32,26 @@ throw new ApiProblemException(

### Built-in problems

- [ExceptionApiProblem](#exceptionapiproblem)
- [ForbiddenProblem](#forbiddenproblem)
- [HttpApiProblem](#httpapiproblem)
- [NotFoundProblem](#notfoundproblem)
- [UnauthorizedProblem](#unauthorizedproblem)
- [ValidationApiProblem](#validationapiproblem)
- [BadRequestProblem](#badrequestproblem)
- [ConflictProblem](#conflictproblem)
- General problems
- [ExceptionApiProblem](#exceptionapiproblem)
- [HttpApiProblem](#httpapiproblem)

- Symfony integration problems
- [ValidationApiProblem](#validationapiproblem)

- Http problems
- 400 [BadRequestProblem](#badrequestproblem)
- 401 [UnauthorizedProblem](#unauthorizedproblem)
- 403 [ForbiddenProblem](#forbiddenproblem)
- 404 [NotFoundProblem](#notfoundproblem)
- 405 [MethodNotAllowedProblem](#methodnotallowedproblem)
- 409 [ConflictProblem](#conflictproblem)
- 412 [PreconditionFailedProblem](#preconditionfailedproblem)
- 415 [UnsupportedMediaTypeProblem](#unsupportedmediatypeproblem)
- 418 [IAmATeapotProblem](#iamateapotproblem)
- 422 [UnprocessableEntityProblem](#unprocessableentityproblem)
- 423 [LockedProblem](#lockedproblem)
- 428 [PreconditionRequiredProblem](#preconditionrequiredproblem)

#### ExceptionApiProblem

Expand Down Expand Up @@ -90,54 +100,69 @@ new ExceptionApiProblem(new \Exception('message', 500));
}
````

#### ForbiddenProblem
#### HttpApiProblem

```php
use Phpro\ApiProblem\Http\ForbiddenProblem;
use Phpro\ApiProblem\Http\HttpApiProblem;

new ForbiddenProblem('Not authorized to access gold.');
new HttpApiProblem(404, ['detail' => 'The book could not be found.']);
```

```json
{
"status": 403,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Forbidden",
"detail": "Not authorized to access gold."
"status": 404,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Not found",
"detail": "The book could not be found."
}
````

#### HttpApiProblem
#### ValidationApiProblem

```sh
composer require symfony/validator:^4.1
```

```php
use Phpro\ApiProblem\Http\HttpApiProblem;
use Phpro\ApiProblem\Http\ValidationApiProblem;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList;

new HttpApiProblem(404, ['detail' => 'The book could not be found.']);
new ValidationApiProblem(new ConstraintViolationList([
new ConstraintViolation('Invalid email', '', [], '', 'email', '', null, '8615ecd9-afcb-479a-9c78-8bcfe260cf2a'),
]));
```

```json
{
"status": 404,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Not found",
"detail": "The book could not be found."
"status": 400,
"type": "https:\/\/symfony.com\/errors\/validation",
"title": "Validation Failed",
"detail": "email: Invalid Email",
"violations": [
{
"propertyPath": "email",
"title": "Invalid email",
"type": "urn:uuid:8615ecd9-afcb-479a-9c78-8bcfe260cf2a"
}
]
}
````

#### NotFoundProblem
#### BadRequestProblem

```php
use Phpro\ApiProblem\Http\NotFoundProblem;
use Phpro\ApiProblem\Http\BadRequestProblem;

new NotFoundProblem('The book with ID 20 could not be found.');
new BadRequestProblem('Bad request. Bad!.');
```

```json
{
"status": 404,
"status": 400,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Not found",
"detail": "The book with ID 20 could not be found."
"title": "Bad Request",
"detail": "Bad request. Bad!"
}
````

Expand All @@ -158,52 +183,54 @@ new UnauthorizedProblem('You are not authorized to access X.');
}
````

#### ValidationApiProblem
#### ForbiddenProblem

```sh
composer require symfony/validator:^4.1
```php
use Phpro\ApiProblem\Http\ForbiddenProblem;

new ForbiddenProblem('Not authorized to access gold.');
```

```json
{
"status": 403,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Forbidden",
"detail": "Not authorized to access gold."
}
````

#### NotFoundProblem

```php
use Phpro\ApiProblem\Http\ValidationApiProblem;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList;
use Phpro\ApiProblem\Http\NotFoundProblem;

new ValidationApiProblem(new ConstraintViolationList([
new ConstraintViolation('Invalid email', '', [], '', 'email', '', null, '8615ecd9-afcb-479a-9c78-8bcfe260cf2a'),
]));
new NotFoundProblem('The book with ID 20 could not be found.');
```

```json
{
"status": 400,
"type": "https:\/\/symfony.com\/errors\/validation",
"title": "Validation Failed",
"detail": "email: Invalid Email",
"violations": [
{
"propertyPath": "email",
"title": "Invalid email",
"type": "urn:uuid:8615ecd9-afcb-479a-9c78-8bcfe260cf2a"
}
]
"status": 404,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Not found",
"detail": "The book with ID 20 could not be found."
}
````

#### BadRequestProblem
#### MethodNotAllowedProblem

```php
use Phpro\ApiProblem\Http\BadRequestProblem;
use Phpro\ApiProblem\Http\MethodNotAllowedProblem;

new BadRequestProblem('Bad request. Bad!.');
new MethodNotAllowedProblem('Only POST and GET allowed.');
veewee marked this conversation as resolved.
Show resolved Hide resolved
```

```json
{
"status": 400,
"status": 405,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Bad Request",
"detail": "Bad request. Bad!"
"title": "Method Not Allowed",
"detail": "Only POST and GET allowed."
}
````

Expand All @@ -223,6 +250,108 @@ new ConflictProblem('Duplicated key for book with ID 20.');
}
````

#### PreconditionFailedProblem

```php
use Phpro\ApiProblem\Http\PreconditionFailedProblem;

new PreconditionFailedProblem('Incorrect entity tag provided.');
```

```json
{
"status": 412,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Precondition Failed",
"detail": "Incorrect entity tag provided."
}
````

#### UnsupportedMediaTypeProblem

```php
use Phpro\ApiProblem\Http\UnsupportedMediaTypeProblem;

new UnsupportedMediaTypeProblem('Please provide valid JSON.');
Landerstraeten marked this conversation as resolved.
Show resolved Hide resolved
```

```json
{
"status": 415,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Unsupported Media Type",
"detail": "Please provide valid JSON."
}
````

#### IAmATeapotProblem
Landerstraeten marked this conversation as resolved.
Show resolved Hide resolved

```php
use Phpro\ApiProblem\Http\IAmATeapotProblem;

new IAmATeapotProblem('More tea please.');
```

```json
{
"status": 418,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "I'm a teapot",
"detail": "More tea please."
}
````

#### UnprocessableEntityProblem

```php
use Phpro\ApiProblem\Http\UnprocessableEntityProblem;

new UnprocessableEntityProblem('Unable to process the contained instructions.');
```

```json
{
"status": 422,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Unprocessable Entity",
"detail": "Unable to process the contained instructions."
}
````

#### LockedProblem

```php
use Phpro\ApiProblem\Http\LockedProblem;

new LockedProblem('This door is locked.');
veewee marked this conversation as resolved.
Show resolved Hide resolved
```

```json
{
"status": 423,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Locked",
"detail": "This door is locked."
}
````

#### PreconditionRequiredProblem

```php
use Phpro\ApiProblem\Http\PreconditionRequiredProblem;

new PreconditionRequiredProblem('If-match header is required.');
veewee marked this conversation as resolved.
Show resolved Hide resolved
```

```json
{
"status": 428,
"type": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html",
"title": "Precondition Required",
"detail": "If-match header is required."
}
````

### Creating your own problem

Creating problem sounds scary right!?
Expand Down
37 changes: 37 additions & 0 deletions spec/Http/IAmATeapotProblemSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace spec\Phpro\ApiProblem\Http;

use Phpro\ApiProblem\Http\HttpApiProblem;
use Phpro\ApiProblem\Http\IAmATeapotProblem;
use PhpSpec\ObjectBehavior;

class IAmATeapotProblemSpec extends ObjectBehavior
{
public function let(): void
{
$this->beConstructedWith('i am a teapot');
}

public function it_is_initializable(): void
{
$this->shouldHaveType(IAmATeapotProblem::class);
}

public function it_is_an_http_api_problem(): void
{
$this->shouldHaveType(HttpApiProblem::class);
}

public function it_can_parse_to_array(): void
{
$this->toArray()->shouldBe([
'status' => 418,
'type' => HttpApiProblem::TYPE_HTTP_RFC,
'title' => HttpApiProblem::getTitleForStatusCode(418),
'detail' => 'i am a teapot',
]);
}
}
Loading