Skip to content

Commit bb6c145

Browse files
Merge pull request #130 from MarcinOrlowski/dev
Release 7.0.0
2 parents 724ebd4 + cf26f4d commit bb6c145

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3330
-1808
lines changed

CHANGES.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ See [compatibility docs](docs/compatibility.md) for details about backward compa
66

77
## CHANGE LOG ##
88

9+
* v7.0.0 (2019-11-22)
10+
* **BACKWARD INCOMPATIBLE CHANGES** ([more info](docs/compatibility.md))
11+
* New, flexible API based on `Builder` pattern (see [docs](docs/compatibility.md) for details).
12+
* Reworked `ExceptionHandlerHelper` configuration. Now, you will be able to easily configure every
13+
HttpException for each HTTP status code you want. Separate `ExceptionHandler::TYPE_HTTP_NOT_FOUND_KEY`
14+
and all related stuff, incl. localization key `http_not_found`, configuration is now replace with more
15+
flexible generic code that provides error messages for all supported HTTP codes from in range `400-599`.
16+
* Added support for external data converters (related part of config changed too).
17+
* Config key `classes` is now (partially) `converter`. Its `method` key is gone and `handler`.
18+
needs to be added now, pointing to the class implementing `ConverterContract` acting as delegate worker.
19+
* Data converter now handles objects implementing `JsonSerializable` and `Arrayable` contracts as well.
20+
921
* v6.3.2 (2019-11-07)
1022
* Added `ResponseBuilder::successWithMessage()` method.
1123
* Entries in `classes` config array can now have `pri` (default 0) to enforce order while
@@ -258,4 +270,3 @@ See [compatibility docs](docs/compatibility.md) for details about backward compa
258270

259271
* v1.0.0 (2016-04-11)
260272
* Initial public release
261-

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
But if all is good and clear then follow common routine:
66

77
* fork the project,
8-
* create new branch,
8+
* checkout `dev` branch (NOT `master`!)
9+
* create your own branch from there,
910
* do your changes,
10-
* add tests covering your changes,
11+
* write unit tests for your changes,
1112
* ensure **ALL** tests pass,
1213
* send pull request,
13-
* glory.
14+
* glory :)

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "marcin-orlowski/laravel-api-response-builder",
33
"description": "Helps building nice, normalized and easy to consume Laravel REST API.",
44
"homepage": "https://github.com/MarcinOrlowski/laravel-api-response-builder",
5-
"version": "6.3.2",
5+
"version": "7.0.0",
66
"keywords": [
77
"laravel",
88
"json",

config/response_builder.php

Lines changed: 82 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,80 +27,116 @@
2727
|
2828
*/
2929
'map' => [
30-
30+
// YOUR_API_CODE => '<MESSAGE_KEY>',
3131
],
3232

3333
/*
3434
|-----------------------------------------------------------------------------------------------------------
35-
| Response Builder classes
35+
| Response Builder data converter
3636
|-----------------------------------------------------------------------------------------------------------
3737
|
3838
*/
39-
'classes' => [
39+
'converter' => [
4040
\Illuminate\Database\Eloquent\Model::class => [
41-
'key' => 'item',
42-
'method' => 'toArray',
43-
'pri' => 0,
41+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ToArrayConverter::class,
42+
// 'key' => 'item',
43+
'pri' => 0,
4444
],
4545
\Illuminate\Support\Collection::class => [
46-
'key' => 'items',
47-
'method' => 'toArray',
48-
'pri' => -1,
46+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ToArrayConverter::class,
47+
// 'key' => 'item',
48+
'pri' => 0,
4949
],
5050
\Illuminate\Database\Eloquent\Collection::class => [
51-
'key' => 'items',
52-
'method' => 'toArray',
53-
'pri' => 0,
51+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ToArrayConverter::class,
52+
// 'key' => 'item',
53+
'pri' => 0,
5454
],
5555
\Illuminate\Http\Resources\Json\JsonResource::class => [
56-
'key' => 'item',
57-
'method' => 'toArray',
58-
'pri' => 0,
56+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ToArrayConverter::class,
57+
// 'key' => 'item',
58+
'pri' => 0,
59+
],
60+
61+
/*
62+
|-----------------------------------------------------------------------------------------------------------
63+
| Converters for generic classes should use lower priority to allow dedicated converters to be used.
64+
|-----------------------------------------------------------------------------------------------------------
65+
*/
66+
\JsonSerializable::class => [
67+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\JsonSerializableConverter::class,
68+
// 'key' => 'item',
69+
'pri' => -10,
5970
],
71+
\Illuminate\Contracts\Support\Arrayable::class => [
72+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ArrayableConverter::class,
73+
// 'key' => 'item',
74+
'pri' => -10,
75+
],
76+
6077
],
6178

6279
/*
6380
|-----------------------------------------------------------------------------------------------------------
64-
| data-to-json encoding options
81+
| Exception handler error codes
6582
|-----------------------------------------------------------------------------------------------------------
6683
|
6784
*/
68-
'encoding_options' => JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE,
85+
'exception_handler' => [
86+
/*
87+
* The following options can be used for each entry specified:
88+
* `api_code` : (int) mandatory api_code to be used for given exception
89+
* `http_code` : (int) optional HTTP code. If not specified, exception's HTTP status code will be used.
90+
* `msg_key` : (string) optional localization string key (ie. 'app.my_error_string') which will be used
91+
* if exception's message is empty. If `msg_key` is not provided, ExceptionHandler will
92+
* fall back to built-in message.
93+
* `msg_enforce`: (boolean) if `true`, then fallback message (either one specified with `msg_key`, or
94+
* built-in one will **always** be used, ignoring exception's message string completely.
95+
* If set to `false` (default) then it will enforce either built-in message (if no
96+
* `msg_key` is set, or message referenced by `msg_key` completely ignoring exception
97+
* message ($ex->getMessage()).
98+
*/
99+
'map' => [
100+
/*
101+
* HTTP Exceptions
102+
* ---------------
103+
* Configure how you want Http Exception to be handled based on its Http status code.
104+
* For each code you need to define at least the `api_code` to be returned in final response.
105+
* Additionally, you can specify `http_code` to be any valid 400-599 HTTP status code, otherwise
106+
* code set in the exception will be used.
107+
*/
108+
// HttpException::class => [
109+
// // used by unauthenticated() to obtain api and http code for the exception
110+
// HttpResponse::HTTP_UNAUTHORIZED => [
111+
// 'api_code' => ApiCodes::YOUR_API_CODE_FOR_UNATHORIZED_EXCEPTION,
112+
// ],
113+
// // Required by ValidationException handler
114+
// HttpResponse::HTTP_UNPROCESSABLE_ENTITY => [
115+
// 'api_code' => ApiCodes::YOUR_API_CODE_FOR_VALIDATION_EXCEPTION,
116+
// ],
117+
// // default handler is mandatory and MUST have both `api_code` and `http_code` set.
118+
// 'default' => [
119+
// 'api_code' => ApiCodes::YOUR_API_CODE_FOR_GENERIC_HTTP_EXCEPTION,
120+
// 'http_code' => HttpResponse::HTTP_BAD_REQUEST,
121+
// ],
122+
// ],
123+
// // This is final exception handler. If ex is not dealt with yet this is its last stop.
124+
// // default handler is mandatory and MUST have both `api_code` and `http_code` set.
125+
// 'default' => [
126+
// 'api_code' => ApiCodes::YOUR_API_CODE_FOR_UNHANDLED_EXCEPTION,
127+
// 'http_code' => HttpResponse::HTTP_INTERNAL_SERVER_ERROR,
128+
// ],
129+
// ],
130+
],
131+
],
69132

70133
/*
71134
|-----------------------------------------------------------------------------------------------------------
72-
| Exception handler error codes
135+
| data-to-json encoding options
73136
|-----------------------------------------------------------------------------------------------------------
74137
|
75138
*/
76-
'exception_handler' => [
77-
'exception' => [
78-
// 'http_not_found' => [
79-
// 'code' => \App\ApiCodes::HTTP_NOT_FOUND(),
80-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
81-
// ],
82-
// 'http_service_unavailable' => [
83-
// 'code' => \App\ApiCodes::HTTP_SERVICE_UNAVAILABLE(),
84-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
85-
// ],
86-
// 'http_exception' => [
87-
// 'code' => \App\ApiCodes::HTTP_EXCEPTION(),
88-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
89-
// ],
90-
// 'uncaught_exception' => [
91-
// 'code' => \App\ApiCodes::UNCAUGHT_EXCEPTION(),
92-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_INTERNAL_SERVER_ERROR,
93-
// ],
94-
// 'authentication_exception' => [
95-
// 'code' => \App\ApiCodes::AUTHENTICATION_EXCEPTION(),
96-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_UNAUTHORIZED,
97-
// ],
98-
// 'validation_exception' => [
99-
// 'code' => \App\ApiCodes::VALIDATION_EXCEPTION(),
100-
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY,
101-
// ],
102-
],
103-
],
139+
'encoding_options' => JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE,
104140

105141
/*
106142
|-----------------------------------------------------------------------------------------------------------

docs/compatibility.md

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
# REST API Response Builder for Laravel #
44

5-
### v6 ###
6-
7-
#### v6.3 ####
8-
* `[BREAKING]` This is backward incompatible change in signature of `ResponseBuilder::buildResponse()`, but it only affects
9-
you if you extend `ResponseBuilder` and provide own implementation to manipulate response object
10-
(see [Manipulating Response Object](docs.md#manipulating-response-object)). If you do not, then you are not affected.
5+
### v7 ###
6+
7+
* `[BREAKING]` As the library API migrated to Builder type of implementation, all the former API methods are now removed from
8+
`ResponseBuilder` class (with exception for `success()` and `error()` methods. While it is strongly recommended to switch
9+
to new, more flexible API, the old API can still be used with aid of `ResponseBuilderLegacy` class, which wraps old API and
10+
uses new API under the hood (see source code of `ResponseBuilderLegacy` for implementation details). To use legacy wrapper
11+
during transition period, simply add `use MarcinOrlowski\ResponseBuilder\ResponseBuilderLegacy as ResponseBuilder` and
12+
you should be all good. NOTE: `ResponseBuilderLegacy` class will be removed in v8.
13+
* `[BREAKING]` The `ExceptionHandler` configuration array structure changed, but it only affects you,
14+
if you have custom configuration for `ExceptionHandlerHelper` (See [configuration docs](config.md) for more information).
15+
If you do not use it or do just use default configuration, then you are not affected.
16+
* `[BREAKING]` Data converter config's `method` key is gone. Now you have to use `handler` and give name of class
17+
that implements `ConverterContract`.
18+
* `[BREAKING]` CONFIG: `classes` key is renamed to `converter`.
19+
* `[Low]` Data converter config's `key` config element is now gone. This change only affects those who use data converting
20+
feature **AND** pass objects directly (i.e. not as part of collection nor array).
1121

12-
#### v6.2 ####
13-
* `[Very Low]` Data conversion logic changed slightly. Now it checks if we have configuration entry matching **exactly** the
14-
object's class name. If not, then we'd try to find if we have any configuration for its parent class.
15-
See [Data Conversion](docs.md#data-conversion) for details.
16-
17-
#### v6.1 ####
18-
* `[Very Low]` Removed ability to define own names for response keys which reduces code complexity and simplifies the
19-
library. From now one you need to stick to default names now (`success`, `code`, `message`, `locale`, `data`).
2022

21-
#### v6.0 ####
23+
### v6 ###
2224

2325
* Requires Laravel 6.0+ and PHP 7.2+
2426
* `[BREAKING]` In previous versions built-in reserved codes were hardcoded and always in range of 1-63 which, in general
@@ -40,7 +42,17 @@
4042
`EX_HTTP_EXCEPTION()`, `EX_UNCAUGHT_EXCEPTION()`, `EX_AUTHENTICATION_EXCEPTION()` and `EX_VALIDATION_EXCEPTION()` that would
4143
return valid API code in currently configured range.
4244
* `[Low]` Removed `response_key_map` configuration option.
45+
46+
* `[Very Low] (v6.1)` Removed ability to define own names for response keys which reduces code complexity and simplifies the
47+
library. From now one you need to stick to default names now (`success`, `code`, `message`, `locale`, `data`).
48+
* `[Very Low] (v6.2)` Data conversion logic changed slightly. Now it checks if we have configuration entry matching **exactly**
49+
the object's class name. If not, then we'd try to find if we have any configuration for its parent class.
50+
See [Data Conversion](docs.md#data-conversion) for details.
51+
* `[BREAKING] (v6.3)` This is backward incompatible change in signature of `ResponseBuilder::buildResponse()`, but it only affects
52+
you if you extend `ResponseBuilder` and provide own implementation to manipulate response object
53+
(see [Manipulating Response Object](docs.md#manipulating-response-object)). If you do not, then you are not affected.
4354

55+
4456
### v5 ###
4557

4658
* No public release.

docs/config.md

Lines changed: 35 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
Available configuration options and its current default values listed in alphabetical order. Please note, that in majority
1515
of use cases it should be perfectly sufficient to just use defaults and only tune the config when needed.
1616

17-
* [classes](#classes)
17+
* [converter](#converter)
1818
* [debug](#debug)
1919
* [encoding_options](#encoding_options)
2020
* [exception_handler](#exception_handler)
@@ -24,24 +24,35 @@
2424

2525
## classes ##
2626

27-
`Response Builder` can auto-convert to be used as response `data`. Create new entry for each class you want to have supported
28-
The entry key is a class name to check passed `data` object against, and configuration elements include:
27+
`Response Builder` can auto-convert to be used as response `data`. The following classes are supported out of the
28+
box:
29+
30+
* `\Illuminate\Database\Eloquent\Model`
31+
* `\Illuminate\Support\Collection`
32+
* `\Illuminate\Database\Eloquent\Collection`
33+
* `\Illuminate\Http\Resources\Json\JsonResource`
34+
35+
Create new entry for each class you want to have supported. The entry key is a full class name (including namespace):
2936

3037
```php
31-
'classes' => [
38+
'converter' => [
3239
Namespace\Classname::class => [
33-
'method' => 'toArray',
34-
'key' => 'items',
35-
'pri' => 0,
40+
'handler' => \MarcinOrlowski\ResponseBuilder\Converters\ToArrayConverter::class,
41+
'key' => 'items',
42+
43+
// Optional paramters
44+
'pri' => 0,
3645
],
3746
],
3847
```
39-
Where `method` is a name of the method to that `ResponseBuilder` should call on the object to obtain array representation of its
40-
internal state, while `key` is a string that will be used as the JSON response as key to array representation.
48+
The `handler` is full name of the class that implements `ConverterContract`. Object of that class will be instantiated
49+
and conversion method will be invked. The `key` is a string that will be used as the JSON response as key to array representation.
4150

42-
**NOTE:** order or entries matters as matching is done in order of appearance and is done using PHP `instanceof`.
51+
All configuration entries are assigned priority `0` which can be changed using `pri` key (integer). This value is used to
52+
sort the entries to ensure that matching order is preserved. Entries with higher priority are matched first etc. This is
53+
very useful when you want to indirect configuration for two classes where additionally second extends first one.
4354
So if you have class `A` and `B` that extends `A` and you want different handling for `B` than you have set for `A`
44-
then `B` related configuration must be put first.
55+
then `B` related configuration must be set with higher priority.
4556

4657
See [Data Conversion](docs.md#data-conversion) docs for closer details wih examples.
4758

@@ -100,54 +111,20 @@ See [Data Conversion](docs.md#data-conversion) docs for closer details wih examp
100111

101112
## exception_handler ##
102113

103-
If you use `ResponseBuilder`'s Exception handler helper, you must map all the exceptions handled to unique api code
104-
from your currently configured range. That allows API calls chaining with proper error failure handling up to the
105-
top client code.
114+
`ResponseBuilder`'s Exception handler helper is plug-and-play helper that will automatically handle
115+
any exception thrown by your code and expose valid JSON response to the client applications. But aside
116+
from error handling, some programmers use exceptions to quickly break the flow and return with additional
117+
information. In such case you may want to assign separate API code to each of these "special" exceptions
118+
and this is where `exception_handler` section comes in.
106119

107-
```php
108-
'exception_handler' => [
109-
'exception' => [
110-
'http_not_found' => [
111-
'code' => \App\ApiCodes::HTTP_NOT_FOUND(),
112-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
113-
],
114-
...
115-
]
116-
]
117-
```
118-
119-
120-
Default exception handling configuration:
121-
122-
```php
123-
'exception_handler' => [
124-
'exception' => [
125-
'http_not_found' => [
126-
'code' => \App\ApiCodes::HTTP_NOT_FOUND(),
127-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
128-
],
129-
'http_service_unavailable' => [
130-
'code' => \App\ApiCodes::HTTP_SERVICE_UNAVAILABLE(),
131-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
132-
],
133-
'http_exception' => [
134-
'code' => \App\ApiCodes::HTTP_EXCEPTION(),
135-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
136-
],
137-
'uncaught_exception' => [
138-
'code' => \App\ApiCodes::UNCAUGHT_EXCEPTION(),
139-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_INTERNAL_SERVER_ERROR,
140-
],
141-
'authentication_exception' => [
142-
'code' => \App\ApiCodes::AUTHENTICATION_EXCEPTION(),
143-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_UNAUTHORIZED,
144-
],
145-
'validation_exception' => [
146-
'code' => \App\ApiCodes::VALIDATION_EXCEPTION(),
147-
'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_UNPROCESSABLE_ENTITY,
148-
],
149-
],
150-
```
120+
Each configuration entry consits of exception class name as a key and parameters array with fields
121+
`api_code` and `http_code`. At runtime, exception handler will look for config entry for particualr
122+
exception class and if there's one, proper handler, dedicated to that exception class, kicks in
123+
and deals with the exception. If no such config exists, `default` handler will be used.
124+
125+
**NOTE:** For now there's no option to specify custom converted as of yet (but that's next step anywya),
126+
so adding own classes to the config same way we did for
127+
`\Symfony\Component\HttpKernel\Exception\HttpException::class` won't work.
151128

152129
## map ##
153130

0 commit comments

Comments
 (0)