Skip to content

Commit 7a440c7

Browse files
Merge pull request #111 from MarcinOrlowski/dev
Release v6.3.1
2 parents 65b5b35 + a753609 commit 7a440c7

19 files changed

+216
-209
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ See [compatibility docs](docs/compatibility.md) for details about backward compa
66

77
## CHANGE LOG ##
88

9+
* 6.3.1 (2019-11-06)
10+
* Fixed config merging helper causing certain user settings to be lost.
11+
* No longer exposes exception class name for message-less exceptions. Fixes #107
12+
* Added test ensuring that user privided config overshadows built-in params.
13+
914
* v6.3.0 (2019-11-02)
1015
* **BACKWARD INCOMPATIBLE CHANGES** ([more info](docs/compatibility.md))
1116
* Signature of `ResponseBuilder::buildResponse()` changed to allow customization of final `message` entry (@hawezo).

composer.json

Lines changed: 6 additions & 6 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.3.0",
5+
"version": "6.3.1",
66
"keywords": [
77
"laravel",
88
"json",
@@ -33,21 +33,21 @@
3333
]
3434
}
3535
},
36-
"require": {
37-
"php": ">=7.2.0",
38-
"laravel/framework": "^6.0"
39-
},
4036
"autoload": {
4137
"classmap": [
4238
"src/",
4339
"tests/",
4440
"tests/Traits/"
4541
]
4642
},
43+
"require": {
44+
"php": ">=7.2.0",
45+
"laravel/framework": "^6.0"
46+
},
4747
"require-dev": {
4848
"marcin-orlowski/coding-standard": "^1.3",
49+
"marcin-orlowski/phpunit-extra-asserts": "^1.0",
4950
"orchestra/testbench": "^4.0",
50-
"laravel/framework": "^6.0",
5151
"phpunit/phpunit": "^8.0",
5252
"phpunit/php-code-coverage": "^7.0",
5353
"codacy/coverage": "^1.0",

src/ApiCodesHelpers.php

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,14 @@ public static function getApiCodeConstants(): array
8080
*/
8181
public static function getMap(): array
8282
{
83-
$map = Config::get(ResponseBuilder::CONF_KEY_MAP, null);
84-
if ($map === null) {
85-
throw new \RuntimeException(sprintf('CONFIG: Missing "%s" key', $map));
83+
$user_map = Config::get(ResponseBuilder::CONF_KEY_MAP, null);
84+
if ($user_map === null) {
85+
throw new \RuntimeException(sprintf('CONFIG: Missing "%s" key', ResponseBuilder::CONF_KEY_MAP));
8686
}
87-
88-
if (!is_array($map)) {
89-
throw new \RuntimeException(sprintf('CONFIG: "%s" must be an array', $map));
87+
if (!is_array($user_map)) {
88+
throw new \RuntimeException(sprintf('CONFIG: "%s" must be an array', ResponseBuilder::CONF_KEY_MAP));
9089
}
91-
92-
/** @noinspection AdditionOperationOnArraysInspection */
93-
return $map + BaseApiCodes::getBaseMap();
90+
return Util::mergeConfig(BaseApiCodes::getBaseMap(), $user_map);
9491
}
9592

9693
/**
@@ -102,7 +99,7 @@ public static function getMap(): array
10299
*
103100
* @throws \InvalidArgumentException If $code is not in allowed range.
104101
*/
105-
public static function getCodeMessageKey($api_code): ?string
102+
public static function getCodeMessageKey(int $api_code): ?string
106103
{
107104
if (!static::isCodeValid($api_code)) {
108105
$min = static::getMinCode();

src/ExceptionHandlerHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ protected static function error(Exception $exception, $exception_config_key, $fa
167167
if ($error_message === '') {
168168
$error_message = Lang::get($key, [
169169
'api_code' => $api_code,
170-
'message' => get_class($exception),
170+
'message' => '???',
171171
]);
172172
}
173173

src/ResponseBuilder.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class ResponseBuilder
6363
public const CONF_KEY_CLASSES = 'response_builder.classes';
6464
public const CONF_KEY_MIN_CODE = 'response_builder.min_code';
6565
public const CONF_KEY_MAX_CODE = 'response_builder.max_code';
66-
public const CONF_KEY_RESPONSE_KEY_MAP = 'response_builder.map';
6766
public const CONF_EXCEPTION_HANDLER_KEY = 'response_builder.exception_handler';
6867

6968
/**

src/ResponseBuilderServiceProvider.php

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ public function boot()
5252
]);
5353
}
5454

55-
/**********************************************************************************************
56-
* Support for multi-dimensional config array. Built-in config merge only supports flat arrays.
57-
*
58-
*/
59-
6055
/**
6156
* Merge the given configuration with the existing configuration.
6257
*
@@ -68,34 +63,7 @@ public function boot()
6863
protected function mergeConfigFrom($path, $key)
6964
{
7065
$config = $this->app['config']->get($key, []);
71-
$this->app['config']->set($key, $this->mergeConfig(require $path, $config));
72-
}
73-
74-
/**
75-
* Merges the configs together and takes multi-dimensional arrays into account.
76-
*
77-
* @param array $original
78-
* @param array $merging
79-
*
80-
* @return array
81-
*/
82-
protected function mergeConfig(array $original, array $merging)
83-
{
84-
$array = array_merge($original, $merging);
85-
foreach ($original as $key => $value) {
86-
if (!is_array($value)) {
87-
continue;
88-
}
89-
if (!array_key_exists($key, $merging)) {
90-
continue;
91-
}
92-
if (is_numeric($key)) {
93-
continue;
94-
}
95-
$array[ $key ] = $this->mergeConfig($value, $merging[ $key ]);
96-
}
97-
98-
return $array;
66+
$this->app['config']->set($key, Util::mergeConfig(require $path, $config));
9967
}
10068

10169
}

src/Util.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace MarcinOrlowski\ResponseBuilder;
5+
6+
/**
7+
* Laravel API Response Builder
8+
*
9+
* @package MarcinOrlowski\ResponseBuilder
10+
*
11+
* @author Marcin Orlowski <mail (#) marcinOrlowski (.) com>
12+
* @copyright 2016-2019 Marcin Orlowski
13+
* @license http://www.opensource.org/licenses/mit-license.php MIT
14+
* @link https://github.com/MarcinOrlowski/laravel-api-response-builder
15+
*/
16+
final class Util
17+
{
18+
/**
19+
* Merges the configs together and takes multi-dimensional arrays into account.
20+
* Support for multi-dimensional config array. Built-in config merge only supports flat arrays.
21+
* Throws \RuntimeException if arrays stucture causes type conflics (i.e. you want to merge
22+
* array with int).
23+
*
24+
* @param array $original Array to merge other array into. Usually default values to overwrite.
25+
* @param array $merging Array with items to be merged into $original, overriding (primitives) or merging
26+
* (arrays) entries in destination array.
27+
*
28+
* @return array
29+
*
30+
* @throws \RuntimeException
31+
*/
32+
public static function mergeConfig(array $original, array $merging): array
33+
{
34+
$array = $original;
35+
foreach ($merging as $m_key => $m_val) {
36+
if (array_key_exists($m_key, $original)) {
37+
$orig_type = gettype($original[ $m_key ]);
38+
$m_type = gettype($m_val);
39+
if ($orig_type !== $m_type) {
40+
throw new \RuntimeException(
41+
"Incompatible types. Cannot merge {$m_type} into {$orig_type} (key '{$m_key}').");
42+
}
43+
44+
if (is_array($merging[ $m_key ])) {
45+
$array[ $m_key ] = static::mergeConfig($original[ $m_key ], $m_val);
46+
} else {
47+
$array[ $m_key ] = $m_val;
48+
}
49+
} else {
50+
$array[ $m_key ] = $m_val;
51+
}
52+
}
53+
54+
return $array;
55+
}
56+
57+
}

src/Validator.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Validator
2828
* Checks if given $val is of type integer
2929
*
3030
* @param string $key Name of the key to be used if exception is thrown.
31-
* @param mixed $var Data to validated.
31+
* @param mixed $var Variable to be asserted.
3232
*
3333
* @return void
3434
*
@@ -42,60 +42,60 @@ public static function assertInt(string $key, $var): void
4242
/**
4343
* Checks if given $val is of type string
4444
*
45-
* @param string $key Name of the key to be used if exception is thrown.
46-
* @param mixed $var Data to validated.
45+
* @param string $name Label or name of the variable to be used in exception message (if thrown).
46+
* @param mixed $var Variable to be asserted.
4747
*
4848
* @return void
4949
*
5050
* @throws \InvalidArgumentException
5151
*/
52-
public static function assertString(string $key, $var): void
52+
public static function assertString(string $name, $var): void
5353
{
54-
self::assertType($key, $var, [self::TYPE_STRING]);
54+
self::assertType($name, $var, [self::TYPE_STRING]);
5555
}
5656

5757
/**
58-
* @param string $key Name of the key to be used if exception is thrown.
59-
* @param mixed $var Data to validated.
60-
* @param int $min Min allowed value (inclusive)
61-
* @param int $max Max allowed value (inclusive)
58+
* @param string $name Label or name of the variable to be used in exception message (if thrown).
59+
* @param mixed $var Variable to be asserted.
60+
* @param int $min Min allowed value (inclusive)
61+
* @param int $max Max allowed value (inclusive)
6262
*
6363
* @return void
6464
*
6565
* @throws \InvalidArgumentException
6666
* @throws \RuntimeException
6767
*/
68-
public static function assertIntRange(string $key, $var, int $min, int $max): void
68+
public static function assertIntRange(string $name, $var, int $min, int $max): void
6969
{
70-
self::assertInt($key, $var);
70+
self::assertInt($name, $var);
7171

7272
if ($min > $max) {
7373
throw new \RuntimeException(
74-
sprintf('%s: Invalid range for "%s". Ensure bound values are not swapped.', __FUNCTION__, $key));
74+
sprintf('%s: Invalid range for "%s". Ensure bound values are not swapped.', __FUNCTION__, $name));
7575
}
7676

7777
if (($min > $var) || ($var > $max)) {
7878
throw new \InvalidArgumentException(
79-
sprintf('Invalid value of "%s" (%d). Must be between %d-%d inclusive.', $key, $var, $min, $max));
79+
sprintf('Invalid value of "%s" (%d). Must be between %d-%d inclusive.', $name, $var, $min, $max));
8080
}
8181
}
8282

8383
/**
8484
* Checks if $item (of name $key) is of type that is include in $allowed_types.
8585
*
86-
* @param string $key
87-
* @param mixed $var
88-
* @param array[string] $allowed_types
86+
* @param string $name Label or name of the variable to be used in exception message (if thrown).
87+
* @param mixed $var Variable to be asserted.
88+
* @param array $allowed_types Array of allowed types for $var, i.e. [Validator::TYPE_INTEGER]
8989
*
9090
* @return void
9191
*
9292
* @throws \InvalidArgumentException
9393
*/
94-
public static function assertType(string $key, $var, array $allowed_types): void
94+
public static function assertType(string $name, $var, array $allowed_types): void
9595
{
9696
$type = gettype($var);
9797
if (!in_array($type, $allowed_types)) {
98-
$msg = sprintf('"%s" must be one of allowed types: %s (%s given)', $key, implode(', ', $allowed_types), gettype($var));
98+
$msg = sprintf('"%s" must be one of allowed types: %s (%s given)', $name, implode(', ', $allowed_types), gettype($var));
9999
throw new \InvalidArgumentException($msg);
100100
}
101101
}

tests/ExceptionHandlerHelper/ExceptionHandlerHelperTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,13 @@ public function testErrorMethodWithDebugTrace(): void
5959
$exception = new \RuntimeException();
6060

6161
$j = json_decode(ExceptionHandlerHelper::render(null, $exception)->getContent(), false);
62-
6362
$this->assertValidResponse($j);
6463
$this->assertNull($j->data);
6564

6665
$key = ResponseBuilder::KEY_DEBUG;
6766
$this->assertObjectHasAttribute($key, $j, sprintf("No '{key}' element in response structure found"));
6867

69-
// Note that we do not check what debug node contains. It's on purpose as whatever ends up there
68+
// Note, that we do not check what debug node contains. It's on purpose as whatever ends up there
7069
// is not generated by us, so may change any time.
7170
}
7271

@@ -186,7 +185,7 @@ protected function doTestSingleException(string $exception_config_key,
186185

187186
$ex_message = trim($exception->getMessage());
188187
if ($ex_message === '') {
189-
$ex_message = get_class($exception);
188+
$ex_message = '???';
190189
}
191190

192191
/** @noinspection PhpUndefinedClassInspection */

tests/ResponseBuilder/ErrorTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,27 @@ public function testErrorMissingMessageMapping(): void
232232
$this->assertNull($j->data);
233233
}
234234

235+
/**
236+
* Checks if overriding built-in code->msg mapping works as expected.
237+
*/
238+
public function testErrorWithCustomMessageConfigForCode(): void
239+
{
240+
// The $msg_key pretends to be regular Laravel localized string
241+
// key (like `api.my_error`). As the string it points is not available
242+
// Laravel will return the key itself, which is perfectly sufficient
243+
// for us to ensure custom config settings are respected.
244+
$msg_key = $this->getRandomString('str_key');
245+
$api_code = $this->min_allowed_code;
246+
\Config::set(ResponseBuilder::CONF_KEY_MAP, [$api_code => $msg_key]);
247+
248+
// Building error response with that api_code
249+
$this->response = ResponseBuilder::error($api_code);
250+
251+
// Should return a response object with our $msg_key as message.
252+
$j = $this->getResponseErrorObject($api_code, ResponseBuilder::DEFAULT_HTTP_CODE_ERROR, $msg_key);
253+
$this->assertEquals($msg_key, $j->message);
254+
}
255+
235256
/**
236257
* Tests buildErrorResponse() fed with not allowed OK api code.
237258
*

0 commit comments

Comments
 (0)