diff --git a/.docs/README.md b/.docs/README.md index 12dc667e..a31e1fa4 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -22,7 +22,7 @@ ## Installation -Simpliest way to register this core API library is via [Nette\DI\CompilerExtension](https://api.nette.org/2.4/Nette.DI.CompilerExtension.html). +Simplest way to register this core API library is via [Nette\DI\CompilerExtension](https://api.nette.org/2.4/Nette.DI.CompilerExtension.html). ``` composer require apitte/core @@ -106,21 +106,21 @@ At the end, open your browser and locate to `localhost//hello/world ## Annotations -| Annotation | Target | Attributes | Description | -|----------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| -| `@Controller` | Class | none | Mark as as type `controller`. | -| `@ControllerId` | Class | value=`{a-z, A-Z, 0-9, _}` | Prefix all children methods ids with `id`. | -| `@ControllerPath` | Class | value=`{a-z, A-Z, 0-9, -_/}` | Prefix all children methods paths with `path`. | -| `@GroupId` | Class | value=`{a-z, A-Z, 0-9, _}` | Prefix all children methods ids with `id`. Can be set only on abstract class. | -| `@GroupPath` | Class | value=`{a-z, A-Z, 0-9, -_/}` | Prefix all children methods paths with `path`. Can be set only on abstract class. | -| `@Id` | Method | value=`{a-z, A-Z, 0-9, _}` | Set `id` to target method. | -| `@Method` | Method | GET, POST, PUT, OPTION, DELETE, HEAD | Set `method` to target method. | -| `@Negotiations` | Method | `@Negotiation` | Group annotation for `@Negotiation`. | -| `@Negotiation` | Method | `suffix={string}`, `default={true/false}`, `renderer={string}` | Define negotiation mode to target method. | -| `@Path` | Method | `value={a-z, A-Z, 0-9, -_/{}}` | Set `path` to target method. A.k.a. URL path. | -| `@RequestParameters` | Method | `@RequestParameter` | Group annotation for `@RequestParameter`. | -| `@RequestParameter` | Method | `name={string}`, `type={int/string/float/bool}`, `description={string}`, `in={path/query}`, `required={true/false}`, `deprecated={true/false}`, `allowEmpty={true/false}` | Define dynamic typed parameter. | -| `@Tag` | Method | `name={string}`, `value={mixed}` | Add `tag` to target method. | +| Annotation | Target | Attributes | Description | +|----------------------|--------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| +| `@Controller` | Class | none | Mark as as type `controller`. | +| `@ControllerId` | Class | value=`{a-z, A-Z, 0-9, _}` | Prefix all children methods ids with `id`. | +| `@ControllerPath` | Class | value=`{a-z, A-Z, 0-9, -_/}` | Prefix all children methods paths with `path`. | +| `@GroupId` | Class | value=`{a-z, A-Z, 0-9, _}` | Prefix all children methods ids with `id`. Can be set only on abstract class. | +| `@GroupPath` | Class | value=`{a-z, A-Z, 0-9, -_/}` | Prefix all children methods paths with `path`. Can be set only on abstract class. | +| `@Id` | Method | value=`{a-z, A-Z, 0-9, _}` | Set `id` to target method. | +| `@Method` | Method | GET, POST, PUT, OPTION, DELETE, HEAD | Set `method` to target method. | +| `@Negotiations` | Method | `@Negotiation` | Group annotation for `@Negotiation`. | +| `@Negotiation` | Method | `suffix={string}`, `default={true/false}`, `renderer={string}` | Define negotiation mode to target method. | +| `@Path` | Method | `value={a-z, A-Z, 0-9, -_/{}}` | Set `path` to target method. A.k.a. URL path. | +| `@RequestParameters` | Method | `@RequestParameter` | Group annotation for `@RequestParameter`. | +| `@RequestParameter` | Method | `name={string}`, `type={scalar/string/int/float/bool/datetime}`, `description={string}`, `in={path/query}`, `required={true/false}`, `deprecated={true/false}`, `allowEmpty={true/false}` | Define dynamic typed parameter. | +| `@Tag` | Method | `name={string}`, `value={mixed}` | Add `tag` to target method. | ## Decorators @@ -150,7 +150,7 @@ Another available plugins are: - [`apitte\debug`](https://github.com/apitte/debug) - adds debugging tools for developing - [`apitte\middlewares`](https://github.com/apitte/middlewares) - adds support for middlewares, depends on [`contributte\middlewares`](https://github.com/contributte/middlewares) -- [`apitte\negotiation`](https://github.com/apitte/negotiation) - adds support for varient content negotiations (.json, .debug, .csv, etc.) +- [`apitte\negotiation`](https://github.com/apitte/negotiation) - adds support for variant content negotiations (.json, .debug, .csv, etc.) - [`apitte\openapi`](https://github.com/apitte/openapi) - adds support for openapi and swagger - [`apitte\events`](https://github.com/apitte/events) - [WIP] - adds support for symfony/event-dispatcher (which is ported into nette via [`contributte\event-dispatcher`](https://github.com/contributte/event-dispatcher)) @@ -168,8 +168,8 @@ Each **decorator** should be registered with tag `apitte.core.decorator`. Each decorator should provide `type` attribute: -- `handle.before` - called before controller method is trigged (after endpoint is matched in router) -- `handle.after` - called after controller method is trigged (after logic in controller) +- `handle.before` - called before controller method is triggered (after endpoint is matched in router) +- `handle.after` - called after controller method is triggered (after logic in controller) - `dispatcher.exception` - called if exception has been occurred Also you should define a priority for better sorting. Default is 10. @@ -207,12 +207,12 @@ api: Apitte\Core\DI\Plugin\CoreMappingPlugin: types: [] request: - validator: + validator: Apitte\Core\Mapping\Validator\NullValidator ``` #### Types -This plugin allows you to define new annotations. +This plugin allows you to define annotation @RequestParameter which validates and converts data type of GET parameters. ```php /** @@ -229,22 +229,42 @@ public function detail(ApiRequest $request) } ``` -It converts request parameters to defined types. By default, you can use `int`, `float`, `string`. Or defined -more types in neon. +Available data types are `scalar`, `string`, `int`, `float`, `bool` and `datetime`. + +- `scalar` + - Tries to automatically convert value to boolean, int or float. + - Returns string, if conversion is not possible. +- `string` + - Simply returns given value. +- `int` + - Converts value to int. + - Could overflow to float if value is bigger than PHP could handle. If it is your case then replace `IntegerTypeMapper` with `StringTypeMapper` +- `float` + - Converts value to float. + - Accepts values which have decimals divided by comma `,` or dot `.` +- `bool` + - Converts `'true'` and `'1'` to `true` + - and `false` and `0` to `false` +- `datetime` + - Converts value to DateTimeImmutable. +- Each of the data types could return null if @RequestParameter(allowEmpty=true) +- If conversion is not possible so API returns HTTP 400 + +You can override these types by your own implementation. ```yaml api: plugins: Apitte\Core\DI\Plugin\CoreMappingPlugin: types: + scalar: Apitte\Core\Mapping\Parameter\ScalarTypeMapper + string: Apitte\Core\Mapping\Parameter\StringTypeMapper int: Apitte\Core\Mapping\Parameter\IntegerTypeMapper float: Apitte\Core\Mapping\Parameter\FloatTypeMapper - string: Apitte\Core\Mapping\Parameter\StringTypeMapper - special: App\MySpecialType + bool: Apitte\Core\Mapping\Parameter\BooleanTypeMapper + datetime: Apitte\Core\Mapping\Parameter\DateTimeTypeMapper ``` -Don't forget to register default one, because filling of `types` overrides default types. - #### Entity ##### RequestMapper @@ -270,7 +290,7 @@ final class UserFilter extends BasicEntity } ``` -And some endoint with `@RequestMapper` annotation. There's a method `ApiRequest::getEntity()`, it gets +And some endpoint with `@RequestMapper` annotation. There's a method `ApiRequest::getEntity()`, it gets the entity from request attributes. So simple, right? ```php @@ -293,7 +313,6 @@ pick the validator you want to. api: plugins: Apitte\Core\DI\Plugin\CoreMappingPlugin: - types: [] request: # By default validator: Apitte\Core\Mapping\Validator\NullValidator diff --git a/README.md b/README.md index 7e73599c..aa5d540d 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ As you can see, the architecture is ultra simple. `ApiRequest` & `ApiResponse` w -The development is sponsored by [Tlapnet](http://www.tlapnet.cz) and a lot of coffeees. Thank you guys! :+1: +The development is sponsored by [Tlapnet](http://www.tlapnet.cz) and a lot of coffees. Thank you guys! :+1: ----- diff --git a/src/DI/Plugin/CoreMappingPlugin.php b/src/DI/Plugin/CoreMappingPlugin.php index 840061ab..29f6d9a3 100644 --- a/src/DI/Plugin/CoreMappingPlugin.php +++ b/src/DI/Plugin/CoreMappingPlugin.php @@ -6,9 +6,11 @@ use Apitte\Core\Decorator\RequestEntityDecorator; use Apitte\Core\Decorator\RequestParametersDecorator; use Apitte\Core\DI\ApiExtension; -use Apitte\Core\Exception\Logical\InvalidStateException; +use Apitte\Core\Mapping\Parameter\BooleanTypeMapper; +use Apitte\Core\Mapping\Parameter\DateTimeTypeMapper; use Apitte\Core\Mapping\Parameter\FloatTypeMapper; use Apitte\Core\Mapping\Parameter\IntegerTypeMapper; +use Apitte\Core\Mapping\Parameter\ScalarTypeMapper; use Apitte\Core\Mapping\Parameter\StringTypeMapper; use Apitte\Core\Mapping\RequestEntityMapping; use Apitte\Core\Mapping\RequestParameterMapping; @@ -23,9 +25,12 @@ class CoreMappingPlugin extends AbstractPlugin /** @var mixed[] */ protected $defaults = [ 'types' => [ + 'scalar' => ScalarTypeMapper::class, + 'string' => StringTypeMapper::class, 'int' => IntegerTypeMapper::class, 'float' => FloatTypeMapper::class, - 'string' => StringTypeMapper::class, + 'bool' => BooleanTypeMapper::class, + 'datetime' => DateTimeTypeMapper::class, ], 'request' => [ 'validator' => NullValidator::class, @@ -44,9 +49,7 @@ public function __construct(PluginCompiler $compiler) public function loadPluginConfiguration(): void { $builder = $this->getContainerBuilder(); - $config = $this->getConfig(); - - if (empty($config['types'])) throw new InvalidStateException('No mapping types provided'); + $config = $this->setupConfig($this->defaults, $this->getConfig()); $builder->addDefinition($this->prefix('request.parameters.decorator')) ->setFactory(RequestParametersDecorator::class) diff --git a/src/Mapping/Parameter/BooleanTypeMapper.php b/src/Mapping/Parameter/BooleanTypeMapper.php new file mode 100644 index 00000000..def4405f --- /dev/null +++ b/src/Mapping/Parameter/BooleanTypeMapper.php @@ -0,0 +1,38 @@ +getName() + )); + } + +} diff --git a/src/Mapping/Parameter/DateTimeTypeMapper.php b/src/Mapping/Parameter/DateTimeTypeMapper.php new file mode 100644 index 00000000..0143dd9a --- /dev/null +++ b/src/Mapping/Parameter/DateTimeTypeMapper.php @@ -0,0 +1,41 @@ +getName() + )); + } + + if ($value !== false) { + return $value; + } + + throw new InvalidArgumentException(sprintf( + 'Parameter "%s" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).', + $parameter->getName() + )); + } + +} diff --git a/src/Mapping/Parameter/FloatTypeMapper.php b/src/Mapping/Parameter/FloatTypeMapper.php index 23912487..0299153c 100644 --- a/src/Mapping/Parameter/FloatTypeMapper.php +++ b/src/Mapping/Parameter/FloatTypeMapper.php @@ -2,19 +2,33 @@ namespace Apitte\Core\Mapping\Parameter; +use Apitte\Core\Exception\Logical\InvalidArgumentException; +use Apitte\Core\Schema\EndpointParameter; + class FloatTypeMapper implements ITypeMapper { /** * @param mixed $value */ - public function normalize($value): ?float + public function normalize($value, EndpointParameter $parameter): ?float { - if ($value === null) { - return $value; + if ($value === null || $value === '') { + return null; + } + + if (is_string($value)) { + $value = str_replace(',', '.', $value); // Accept also comma as decimal separator + } + + if (is_float($value) || is_int($value) || (is_string($value) && preg_match('#^-?[0-9]*[.]?[0-9]+\z#', $value))) { + return (float) $value; } - return (float) $value; + throw new InvalidArgumentException(sprintf( + 'Parameter "%s" should be of type float or integer.', + $parameter->getName() + )); } } diff --git a/src/Mapping/Parameter/ITypeMapper.php b/src/Mapping/Parameter/ITypeMapper.php index 121db34c..d7566188 100644 --- a/src/Mapping/Parameter/ITypeMapper.php +++ b/src/Mapping/Parameter/ITypeMapper.php @@ -2,6 +2,8 @@ namespace Apitte\Core\Mapping\Parameter; +use Apitte\Core\Schema\EndpointParameter; + interface ITypeMapper { @@ -9,6 +11,6 @@ interface ITypeMapper * @param mixed $value * @return mixed */ - public function normalize($value); + public function normalize($value, EndpointParameter $parameter); } diff --git a/src/Mapping/Parameter/IntegerTypeMapper.php b/src/Mapping/Parameter/IntegerTypeMapper.php index b4338b8c..d088c5af 100644 --- a/src/Mapping/Parameter/IntegerTypeMapper.php +++ b/src/Mapping/Parameter/IntegerTypeMapper.php @@ -2,19 +2,29 @@ namespace Apitte\Core\Mapping\Parameter; +use Apitte\Core\Exception\Logical\InvalidArgumentException; +use Apitte\Core\Schema\EndpointParameter; + class IntegerTypeMapper implements ITypeMapper { /** * @param mixed $value */ - public function normalize($value): ?int + public function normalize($value, EndpointParameter $parameter): ?int { - if ($value === null) { - return $value; + if ($value === null || $value === '') { + return null; + } + + if (is_int($value) || (is_string($value) && preg_match('#^-?[0-9]+\z#', $value))) { + return (int) $value; } - return (int) $value; + throw new InvalidArgumentException(sprintf( + 'Parameter "%s" should be of type integer.', + $parameter->getName() + )); } } diff --git a/src/Mapping/Parameter/ScalarTypeMapper.php b/src/Mapping/Parameter/ScalarTypeMapper.php new file mode 100644 index 00000000..8bce9577 --- /dev/null +++ b/src/Mapping/Parameter/ScalarTypeMapper.php @@ -0,0 +1,46 @@ +normalize($value); + $normalizedValue = $mapper->normalize($value, $parameter); // Update requests $requestParameters[$parameter->getName()] = $normalizedValue; @@ -111,7 +111,7 @@ public function map(ServerRequestInterface $request, ResponseInterface $response } // Normalize value - $normalizedValue = $mapper->normalize($value); + $normalizedValue = $mapper->normalize($value, $parameter); // Update requests $cookieParams[$parameter->getName()] = $normalizedValue; @@ -139,7 +139,7 @@ public function map(ServerRequestInterface $request, ResponseInterface $response throw new InvalidStateException(sprintf('Parameter "%s" should not be empty', $parameter->getName())); } - $normalizedValues[$index] = $mapper->normalize($value); + $normalizedValues[$index] = $mapper->normalize($value, $parameter); } // Update requests diff --git a/src/Schema/EndpointParameter.php b/src/Schema/EndpointParameter.php index dc52d165..63dfda02 100644 --- a/src/Schema/EndpointParameter.php +++ b/src/Schema/EndpointParameter.php @@ -11,7 +11,6 @@ final class EndpointParameter public const TYPE_FLOAT = 'float'; public const TYPE_BOOLEAN = 'bool'; public const TYPE_DATETIME = 'datetime'; - public const TYPE_OBJECT = 'object'; public const TYPES = [ self::TYPE_SCALAR, @@ -20,7 +19,6 @@ final class EndpointParameter self::TYPE_FLOAT, self::TYPE_BOOLEAN, self::TYPE_DATETIME, - self::TYPE_OBJECT, ]; public const IN_QUERY = 'query'; diff --git a/tests/cases/Mapping/Parameter/BooleanTypeMapper.phpt b/tests/cases/Mapping/Parameter/BooleanTypeMapper.phpt new file mode 100644 index 00000000..16c920b5 --- /dev/null +++ b/tests/cases/Mapping/Parameter/BooleanTypeMapper.phpt @@ -0,0 +1,45 @@ +normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + Assert::same(true, $mapper->normalize(true, $parameter)); + Assert::same(true, $mapper->normalize('true', $parameter)); + Assert::same(true, $mapper->normalize('1', $parameter)); + Assert::same(false, $mapper->normalize(false, $parameter)); + Assert::same(false, $mapper->normalize('false', $parameter)); + Assert::same(false, $mapper->normalize('0', $parameter)); + } + + public function testFail(): void + { + $mapper = new BooleanTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_BOOLEAN); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('string', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type boolean. Pass "true" or "1" for true and "false" or "0" for false.'); + } + +} + +(new TestBooleanTypeMapper())->run(); diff --git a/tests/cases/Mapping/Parameter/DateTimeTypeMapper.phpt b/tests/cases/Mapping/Parameter/DateTimeTypeMapper.phpt new file mode 100644 index 00000000..8a3ad3b0 --- /dev/null +++ b/tests/cases/Mapping/Parameter/DateTimeTypeMapper.phpt @@ -0,0 +1,65 @@ +normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + + $datetime = $mapper->normalize('2010-12-07T23:00:00Z', $parameter); + Assert::type(DateTimeImmutable::class, $datetime); + + $datetime = $mapper->normalize('2010-12-07T23:00:00+01:00', $parameter); + Assert::type(DateTimeImmutable::class, $datetime); + + $datetime = $mapper->normalize((new DateTime('now'))->format(DATE_ATOM), $parameter); + Assert::type(DateTimeImmutable::class, $datetime); + } + + public function testFail(): void + { + $mapper = new DateTimeTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_BOOLEAN); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('string', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).'); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize(true, $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).'); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('7.12.2010 23:00:00', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).'); + + // Unfortunately not supported by PHP + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('2010-12-07T23:00:00.000Z', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).'); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('2010-12-07T23:00:00', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type datetime in format ISO 8601 (Y-m-d\TH:i:sP).'); + } + +} + +(new TestDateTimeTypeMapper())->run(); diff --git a/tests/cases/Mapping/Parameter/FloatTypeMapper.phpt b/tests/cases/Mapping/Parameter/FloatTypeMapper.phpt index 1f444545..4bdf15cd 100644 --- a/tests/cases/Mapping/Parameter/FloatTypeMapper.phpt +++ b/tests/cases/Mapping/Parameter/FloatTypeMapper.phpt @@ -6,22 +6,36 @@ require_once __DIR__ . '/../../../bootstrap.php'; +use Apitte\Core\Exception\Logical\InvalidArgumentException; use Apitte\Core\Mapping\Parameter\FloatTypeMapper; +use Apitte\Core\Schema\EndpointParameter; use Tester\Assert; use Tester\TestCase; final class TestFloatTypeMapper extends TestCase { - public function testNormalize(): void + public function testOk(): void { - $floatTypeMapper = new FloatTypeMapper(); + $mapper = new FloatTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_FLOAT); + + Assert::same(null, $mapper->normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + Assert::same(0.0, $mapper->normalize(0, $parameter)); + Assert::same(13.0, $mapper->normalize('13', $parameter)); + Assert::same(1.99, $mapper->normalize('1.99', $parameter)); + Assert::same(-10.0, $mapper->normalize('-10', $parameter)); + } + + public function testFail(): void + { + $mapper = new FloatTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_FLOAT); - Assert::same(null, $floatTypeMapper->normalize(null)); - Assert::same(0.0, $floatTypeMapper->normalize(0)); - Assert::same(0.33, $floatTypeMapper->normalize('0.33')); - Assert::same(1.99, $floatTypeMapper->normalize('1.99')); - Assert::same(-10.0, $floatTypeMapper->normalize('-10')); + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('string', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type float or integer.'); } } diff --git a/tests/cases/Mapping/Parameter/IntegerTypeMapper.phpt b/tests/cases/Mapping/Parameter/IntegerTypeMapper.phpt index 2058161a..86b7770f 100644 --- a/tests/cases/Mapping/Parameter/IntegerTypeMapper.phpt +++ b/tests/cases/Mapping/Parameter/IntegerTypeMapper.phpt @@ -6,22 +6,39 @@ require_once __DIR__ . '/../../../bootstrap.php'; +use Apitte\Core\Exception\Logical\InvalidArgumentException; use Apitte\Core\Mapping\Parameter\IntegerTypeMapper; +use Apitte\Core\Schema\EndpointParameter; use Tester\Assert; use Tester\TestCase; final class TestIntegerTypeMapper extends TestCase { - public function testNormalize(): void + public function testOk(): void { - $floatTypeMapper = new IntegerTypeMapper(); + $mapper = new IntegerTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_INTEGER); + + Assert::same(null, $mapper->normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + Assert::same(0, $mapper->normalize(0, $parameter)); + Assert::same(13, $mapper->normalize('13', $parameter)); + Assert::same(-10, $mapper->normalize('-10', $parameter)); + } + + public function testFail(): void + { + $mapper = new IntegerTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_INTEGER); + + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('string', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type integer.'); - Assert::same(null, $floatTypeMapper->normalize(null)); - Assert::same(0, $floatTypeMapper->normalize(0)); - Assert::same(0, $floatTypeMapper->normalize('0.33')); - Assert::same(1, $floatTypeMapper->normalize('1.99')); - Assert::same(-10, $floatTypeMapper->normalize('-10')); + Assert::exception(function () use ($mapper, $parameter): void { + $mapper->normalize('1.99', $parameter); + }, InvalidArgumentException::class, 'Parameter "foo" should be of type integer.'); } } diff --git a/tests/cases/Mapping/Parameter/ScalarTypeMapper.phpt b/tests/cases/Mapping/Parameter/ScalarTypeMapper.phpt new file mode 100644 index 00000000..a700e89b --- /dev/null +++ b/tests/cases/Mapping/Parameter/ScalarTypeMapper.phpt @@ -0,0 +1,50 @@ +normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + + // Booleans + Assert::same(true, $mapper->normalize(true, $parameter)); + Assert::same(true, $mapper->normalize('true', $parameter)); + Assert::same(true, $mapper->normalize('1', $parameter)); + Assert::same(false, $mapper->normalize(false, $parameter)); + Assert::same(false, $mapper->normalize('false', $parameter)); + Assert::same(false, $mapper->normalize('0', $parameter)); + + // Floats + Assert::same(0.0, $mapper->normalize(0.0, $parameter)); + Assert::same(13.0, $mapper->normalize('13.0', $parameter)); + Assert::same(-10.0, $mapper->normalize('-10.0', $parameter)); + + // Integers + Assert::same(0, $mapper->normalize(0, $parameter)); + Assert::same(13, $mapper->normalize('13', $parameter)); + Assert::same(-10, $mapper->normalize('-10', $parameter)); + + // Strings + Assert::same('string', $mapper->normalize('string', $parameter)); + } + +} + +(new TestScalarTypeMapper())->run(); diff --git a/tests/cases/Mapping/Parameter/StringTypeMapper.phpt b/tests/cases/Mapping/Parameter/StringTypeMapper.phpt index 05770731..543c8b9a 100644 --- a/tests/cases/Mapping/Parameter/StringTypeMapper.phpt +++ b/tests/cases/Mapping/Parameter/StringTypeMapper.phpt @@ -7,21 +7,24 @@ require_once __DIR__ . '/../../../bootstrap.php'; use Apitte\Core\Mapping\Parameter\StringTypeMapper; +use Apitte\Core\Schema\EndpointParameter; use Tester\Assert; use Tester\TestCase; final class TestStringTypeMapper extends TestCase { - public function testNormalize(): void + public function testOk(): void { - $floatTypeMapper = new StringTypeMapper(); - - Assert::same(null, $floatTypeMapper->normalize(null)); - Assert::same('0', $floatTypeMapper->normalize(0)); - Assert::same('0.33', $floatTypeMapper->normalize(0.33)); - Assert::same('1.99', $floatTypeMapper->normalize(1.99)); - Assert::same('-10', $floatTypeMapper->normalize(-10)); + $mapper = new StringTypeMapper(); + $parameter = new EndpointParameter('foo', EndpointParameter::TYPE_STRING); + + Assert::same(null, $mapper->normalize(null, $parameter)); + Assert::same(null, $mapper->normalize('', $parameter)); + Assert::same('0', $mapper->normalize(0, $parameter)); + Assert::same('0.33', $mapper->normalize(0.33, $parameter)); + Assert::same('1.99', $mapper->normalize(1.99, $parameter)); + Assert::same('-10', $mapper->normalize(-10, $parameter)); } }