Skip to content

Commit 0769753

Browse files
Merge pull request #94 from MarcinOrlowski/dev
Release v6.2.1
2 parents 86921ac + c4b6a58 commit 0769753

35 files changed

+793
-461
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
vendor/
33
composer.lock
44
.phpunit.result.cache
5+
.php_cs.cache

.travis.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ php:
2424
env:
2525
- LARAVEL_VERSION="6.0"
2626
- LARAVEL_VERSION="6.2"
27+
- LARAVEL_VERSION="6.3"
2728

2829
#matrix:
2930
# exclude:
@@ -35,17 +36,14 @@ cache:
3536
- ${HOME}/.composer/cache/files
3637

3738
before_script:
38-
# Some PHP versions does not feature xdebug so we need to disable the coverage
39-
# - if [ ${TRAVIS_PHP_VERSION} != 7.2 && ${TRAVIS_PHP_VERSION} != 7.3 ]; then phpenv config-rm xdebug.ini ; fi
4039
- cp -f "travis/composer-${LARAVEL_VERSION}.json" composer.json
4140
- FLAGS="--prefer-dist"
4241
- composer install "${FLAGS}"
43-
# We need to tweak signature so setUp() and tearDown() and strip ": void" otherwise PHP will fail complaining
44-
# This affects Laravel 5.8+ as they pull recent Orchestra that features method signature changes.
45-
- "if [[ ${LARAVEL_VERSION} == 6.0 ]]; then sed -i 's/): void/)/' vendor/orchestra/testbench-core/src/TestCase.php ; fi"
46-
- "if [[ ${LARAVEL_VERSION} == 6.0 ]]; then sed -i 's/): void/)/' vendor/phpunit/phpunit/src/Framework/TestCase.php ; fi"
47-
- "if [[ ${LARAVEL_VERSION} == 6.2 ]]; then sed -i 's/): void/)/' vendor/orchestra/testbench-core/src/TestCase.php ; fi"
48-
- "if [[ ${LARAVEL_VERSION} == 6.2 ]]; then sed -i 's/): void/)/' vendor/phpunit/phpunit/src/Framework/TestCase.php ; fi"
42+
# We need to tweak signature of setUp() and tearDown() methods and strip ": void" otherwise PHP will
43+
# fail complaining. This affects Laravel 5.8+ and 6.x (up to 6.3 for now) as they pull recent Orchestra
44+
# that features method signature changes.
45+
- "sed -i 's/): void/)/' vendor/orchestra/testbench-core/src/TestCase.php"
46+
- "sed -i 's/): void/)/' vendor/phpunit/phpunit/src/Framework/TestCase.php"
4947

5048
script:
5149
- vendor/bin/phpunit --configuration phpunit.xml --coverage-clover /tmp/coverage.xml
@@ -55,4 +53,3 @@ after_success:
5553
- if [[ -f /tmp/coverage.xml ]]; then vendor/bin/codacycoverage clover /tmp/coverage.xml ; fi
5654
# Scrunitizer-CI
5755
- if [[ -f /tmp/coverage.xml ]]; then wget --output-document=/tmp/ocular.phar https://scrutinizer-ci.com/ocular.phar && php /tmp/ocular.phar code-coverage:upload --format=php-clover /tmp/coverage.xml ; fi
58-

CHANGES.md

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

77
## CHANGE LOG ##
88

9+
* 6.2.1 (2019-10-21)
10+
* Added Laravel 6.3 to Travis-CI unit tests.
11+
* Splitted tests into separate folders per class tested.
12+
* ExceptionHandler no longer tries to enforce UTF-8 on exception message.
13+
914
* 6.2.0 (2019-10-19)
1015
* Changed how auto-converter checks for supported classes (see [Data Conversion](docs/docs.md#data-conversion))
1116
* Data conversion now supports [JsonResource](https://laravel.com/docs/6.0/eloquent-resources) data class.
1217
* Added unit test for `ResponseBuilderServiceProvider::mergeConfg()`.
1318
* Moved data conversion code to separate `Converter` class.
1419
* Added `LICENSE.md` file.
15-
* Added Laravel 6.2 to Travis tests.
20+
* Added Laravel 6.2 to Travis-CI unit tests.
1621
* Added unit tests for translation files.
1722

1823
* 6.1.2 (2019-10-02)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797

9898
* Easy to use,
9999
* [Stable and production ready](https://travis-ci.org/MarcinOrlowski/laravel-api-response-builder),
100-
* Laravel compatibility: v6.0, v6.2 (see [legacy support](docs/legacy.md) for support for older versions),
100+
* Laravel compatibility: v6.x (see [legacy support](docs/legacy.md) for support for older versions),
101101
* Supports Laravel [auto-discovery](https://medium.com/@taylorotwell/package-auto-discovery-in-laravel-5-5-ea9e3ab20518),
102102
* Configurable (with ready-to-use defaults),
103103
* Localization support,

composer.json

Lines changed: 4 additions & 3 deletions
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.2.0",
5+
"version": "6.2.1",
66
"keywords": [
77
"laravel",
88
"json",
@@ -45,11 +45,12 @@
4545
]
4646
},
4747
"require-dev": {
48-
"marcin-orlowski/coding-standard": "^1.1",
48+
"marcin-orlowski/coding-standard": "^1.3",
4949
"orchestra/testbench": "^4.0",
5050
"laravel/framework": "^6.0",
5151
"phpunit/phpunit": "^8.0",
5252
"phpunit/php-code-coverage": "^7.0",
53-
"codacy/coverage": "^1.0"
53+
"codacy/coverage": "^1.0",
54+
"friendsofphp/php-cs-fixer": "^2.15"
5455
}
5556
}

config/response_builder.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,27 @@
7373
'exception' => [
7474
// 'http_not_found' => [
7575
// 'code' => \App\ApiCodes::HTTP_NOT_FOUND(),
76-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
76+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
7777
// ],
7878
// 'http_service_unavailable' => [
7979
// 'code' => \App\ApiCodes::HTTP_SERVICE_UNAVAILABLE(),
80-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
80+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
8181
// ],
8282
// 'http_exception' => [
8383
// 'code' => \App\ApiCodes::HTTP_EXCEPTION(),
84-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_BAD_REQUEST,
84+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST,
8585
// ],
8686
// 'uncaught_exception' => [
8787
// 'code' => \App\ApiCodes::UNCAUGHT_EXCEPTION(),
88-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_INTERNAL_SERVER_ERROR,
88+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_INTERNAL_SERVER_ERROR,
8989
// ],
9090
// 'authentication_exception' => [
9191
// 'code' => \App\ApiCodes::AUTHENTICATION_EXCEPTION(),
92-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_UNAUTHORIZED,
92+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_UNAUTHORIZED,
9393
// ],
9494
// 'validation_exception' => [
9595
// 'code' => \App\ApiCodes::VALIDATION_EXCEPTION(),
96-
// 'http_code' => Symfony\Component\HttpFoundation\Response\::HTTP_UNPROCESSABLE_ENTITY,
96+
// 'http_code' => Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY,
9797
// ],
9898
],
9999
],

src/ApiCodesHelpers.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,14 @@ public static function getCodeMessageKey($api_code): ?string
115115
return $map[ $api_code ] ?? null;
116116
}
117117

118-
public static function isCodeValid($code): bool
118+
/**
119+
* Checks if given API $code can be used in current configuration.
120+
*
121+
* @param int $code API code to validate
122+
*
123+
* @return bool
124+
*/
125+
public static function isCodeValid(int $code): bool
119126
{
120127
return ($code === 0) || (($code >= static::getMinCode()) && ($code <= static::getMaxCode()));
121128
}
@@ -126,6 +133,8 @@ public static function isCodeValid($code): bool
126133
* @param int $internal_code
127134
*
128135
* @return int
136+
*
137+
* @throws \InvalidArgumentException
129138
*/
130139
protected static function getCodeForInternalOffset(int $internal_code): int
131140
{

src/Converter.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
class Converter
2424
{
2525
/**
26-
* @var array|null
26+
* @var array
2727
*/
2828
protected $classes;
2929

3030
/**
3131
* Converter constructor.
32+
*
33+
* @throws \RuntimeException
3234
*/
3335
public function __construct()
3436
{
@@ -44,23 +46,24 @@ public function __construct()
4446
/**
4547
* Returns local copy of configuration mapping for the classes.
4648
*
47-
* @return array|null
49+
* @return array
4850
*/
49-
public function getClasses(): ?array
51+
public function getClasses(): array
5052
{
5153
return $this->classes;
5254
}
5355

5456
/**
5557
* Checks if we have "classes" mapping configured for $data object class.
5658
* Returns @true if there's valid config for this class.
59+
* Throws \RuntimeException if there's no config "classes" mapping entryfor this object configured.
60+
* Throws \InvalidArgumentException if No data conversion mapping configured for given class.
5761
*
5862
* @param object $data Object to check mapping for.
5963
*
6064
* @return array
6165
*
62-
* @throws \RuntimeException if there's no config "classes" mapping entry
63-
* for this object configured.
66+
* @throws \InvalidArgumentException
6467
*/
6568
protected function getClassMappingConfigOrThrow(object $data): array
6669
{
@@ -90,9 +93,11 @@ protected function getClassMappingConfigOrThrow(object $data): array
9093
/**
9194
* We need to prepare source data
9295
*
93-
* @param null $data
96+
* @param object|array|null $data
9497
*
9598
* @return array|null
99+
*
100+
* @throws \InvalidArgumentException
96101
*/
97102
public function convert($data = null): ?array
98103
{
@@ -118,6 +123,8 @@ public function convert($data = null): ?array
118123
* @param array $data array to recursively convert known elements of
119124
*
120125
* @return array
126+
*
127+
* @throws \RuntimeException
121128
*/
122129
protected function convertArray(array $data): array
123130
{

src/ExceptionHandlerHelper.php

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,26 @@ protected function unauthenticated(/** @scrutinizer ignore-unused */ $request, A
101101
/**
102102
* Process singe error and produce valid API response
103103
*
104-
* @param Exception $exception Exception to be processed
105-
* @param string $exception_type Category of the exception
106-
* @param integer $default_api_code API code to return
107-
* @param integer $default_http_code HTTP code to return
104+
* @param Exception $exception Exception to be processed
105+
* @param string $exception_config_key Category of the exception
106+
* @param integer $fallback_api_code API code to return
107+
* @param integer $fallback_http_code HTTP code to return
108108
*
109109
* @return HttpResponse
110110
*/
111-
protected static function error(Exception $exception, $exception_type, $default_api_code,
112-
$default_http_code = ResponseBuilder::DEFAULT_HTTP_CODE_ERROR): HttpResponse
111+
protected static function error(Exception $exception, $exception_config_key, $fallback_api_code,
112+
$fallback_http_code = ResponseBuilder::DEFAULT_HTTP_CODE_ERROR): HttpResponse
113113
{
114114
// common prefix for config key
115-
$base_config = 'response_builder.exception_handler.exception';
115+
$base_config_key = sprintf('%s.exception', ResponseBuilder::CONF_EXCEPTION_HANDLER_KEY);
116116

117-
$api_code = Config::get("{$base_config}.{$exception_type}.code", $default_api_code);
118-
$http_code = Config::get("{$base_config}.{$exception_type}.http_code", $default_http_code);
117+
// get API and HTTP codes from exception handler config or use fallback values if no such
118+
// config fields are set.
119+
$api_code = Config::get("{$base_config_key}.{$exception_config_key}.code", $fallback_api_code);
120+
$http_code = Config::get("{$base_config_key}.{$exception_config_key}.http_code", $fallback_http_code);
119121

120122
// check if we now have valid HTTP error code for this case or need to make one up.
121-
if ($http_code === 0) {
123+
if ($http_code < ResponseBuilder::ERROR_HTTP_CODE_MIN) {
122124
// no code, let's try to get the exception status
123125
$http_code = ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpException)
124126
? $exception->getStatusCode()
@@ -127,7 +129,7 @@ protected static function error(Exception $exception, $exception_type, $default_
127129

128130
// can it be considered valid HTTP error code?
129131
if ($http_code < ResponseBuilder::ERROR_HTTP_CODE_MIN) {
130-
$http_code = $default_http_code;
132+
$http_code = $fallback_http_code;
131133
}
132134

133135
// let's figure out what event we are handling now
@@ -141,55 +143,30 @@ protected static function error(Exception $exception, $exception_type, $default_
141143
];
142144
$base_api_code = BaseApiCodes::NO_ERROR_MESSAGE();
143145
foreach ($known_codes as $item_config_key => $item_api_code) {
144-
if ($api_code === Config::get("{$base_config}.{$item_config_key}.code", $item_api_code)) {
146+
if ($api_code === Config::get("{$base_config_key}.{$item_config_key}.code", $item_api_code)) {
145147
$base_api_code = $api_code;
146148
break;
147149
}
148150
}
149151

150152
/** @var array|null $data Optional payload to return */
151153
$data = null;
152-
if ($api_code === Config::get("{$base_config}.validation_exception.code", BaseApiCodes::EX_VALIDATION_EXCEPTION())) {
154+
if ($api_code === Config::get("{$base_config_key}.validation_exception.code", BaseApiCodes::EX_VALIDATION_EXCEPTION())) {
153155
/** @var ValidationException $exception */
154156
$data = [ResponseBuilder::KEY_MESSAGES => $exception->validator->errors()->messages()];
155157
}
156158

157159
$key = BaseApiCodes::getCodeMessageKey($api_code) ?? BaseApiCodes::getCodeMessageKey($base_api_code);
158160

159161
// let's build error error_message
160-
$ex_message = trim($exception->getMessage());
161-
162-
// ensure we won't fail due to exception incorect encoding
163-
// TODO(orlowski): Is this really needed?
164-
if (!mb_check_encoding($ex_message, 'UTF-8')) {
165-
// let's check there's iconv and mb_string available
166-
if (function_exists('iconv') && function_exists('mb_detec_encoding')) {
167-
$ex_message = iconv(mb_detect_encoding($ex_message, mb_detect_order(), true), 'UTF-8', $ex_message);
168-
} else {
169-
// lame fallback, in case there's no iconv/mb_string installed
170-
$ex_message = htmlspecialchars_decode(htmlspecialchars($ex_message, ENT_SUBSTITUTE, 'UTF-8'));
171-
}
172-
}
173-
174-
/** @var string $error_message final error error_message */
175-
$error_message = $ex_message;
176-
$ex_message = get_class($exception);
177-
178-
if ($ex_message === '') {
179-
$error_message = Lang::get($key, [
180-
'api_code' => $api_code,
181-
'error_message' => $ex_message,
182-
'message' => get_class($exception),
183-
]);
184-
}
162+
$error_message = $exception->getMessage();
185163

186164
// if we do not have any error_message in the hand yet, we need to fall back to built-in string configured
187165
// for this particular exception.
188166
if ($error_message === '') {
189167
$error_message = Lang::get($key, [
190-
'api_code' => $api_code,
191-
'error_message' => $ex_message,
192-
'message' => get_class($exception),
168+
'api_code' => $api_code,
169+
'message' => get_class($exception),
193170
]);
194171
}
195172

src/ResponseBuilder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class ResponseBuilder
5656
public const CONF_KEY_MIN_CODE = 'response_builder.min_code';
5757
public const CONF_KEY_MAX_CODE = 'response_builder.max_code';
5858
public const CONF_KEY_RESPONSE_KEY_MAP = 'response_builder.map';
59+
public const CONF_EXCEPTION_HANDLER_KEY = 'response_builder.exception_handler';
5960

6061
/**
6162
* Default keys to be used by exception handler while adding debug information

0 commit comments

Comments
 (0)