Skip to content

Commit

Permalink
Added support for non required const-properties (#82)
Browse files Browse the repository at this point in the history
* Added support for non required const-properties

* Replaced interface to abstract class

* Added implementation of implicit null for constant properties

* Changed the check setRequired

* Remove modelData from check conditions

* Added call generateValidators() and unit test

* Replaced check with three conditions

* Ensured the presence of the $modelData variable in the templates, added some unit tests

---------

Co-authored-by: Kolodkin Valentin <kolodkin@playkot.com>
  • Loading branch information
kolodkinvalentin and Kolodkin Valentin authored Sep 19, 2024
1 parent 1bb1374 commit 83acf61
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/Model/Validator/ExtractedMethodValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function __construct(
public function getCode(): string
{
$renderHelper = new RenderHelper($this->generatorConfiguration);
return "private function {$this->validator->getExtractedMethodName()}(&\$value): void {
return "private function {$this->validator->getExtractedMethodName()}(&\$value, \$modelData): void {
{$this->validator->getValidatorSetUp()}
if ({$this->validator->getCheck()}) {
Expand Down
33 changes: 23 additions & 10 deletions src/PropertyProcessor/Property/ConstProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
use PHPModelGenerator\Model\Property\PropertyType;
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
use PHPModelGenerator\Model\Validator\PropertyValidator;
use PHPModelGenerator\PropertyProcessor\PropertyProcessorInterface;
use PHPModelGenerator\Utils\RenderHelper;
use PHPModelGenerator\Utils\TypeConverter;

/**
* Class ConstProcessor
*
* @package PHPModelGenerator\PropertyProcessor\Property
*/
class ConstProcessor implements PropertyProcessorInterface
class ConstProcessor extends AbstractPropertyProcessor
{
/**
* @inheritdoc
Expand All @@ -34,13 +34,26 @@ public function process(string $propertyName, JsonSchema $propertySchema): Prope
$json['description'] ?? '',
);

return $property
->setRequired(true)
->addValidator(new PropertyValidator(
$property,
'$value !== ' . var_export($json['const'], true),
InvalidConstException::class,
[$json['const']],
));
$property->setRequired($this->propertyMetaDataCollection->isAttributeRequired($propertyName));

$check = match(true) {
$property->isRequired()
=> '$value !== ' . var_export($json['const'], true),
$this->isImplicitNullAllowed($property)
=> '!in_array($value, ' . RenderHelper::varExportArray([$json['const'], null]) . ', true)',
default
=> "array_key_exists('{$property->getName()}', \$modelData) && \$value !== " . var_export($json['const'], true),
};

$property->addValidator(new PropertyValidator(
$property,
$check,
InvalidConstException::class,
[$json['const']],
));

$this->generateValidators($property, $propertySchema);

return $property;
}
}
2 changes: 1 addition & 1 deletion src/Templates/Validator/AdditionalProperties.phptpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function () use ($properties, &$invalidProperties) {
(function () use ($properties, &$invalidProperties, $modelData) {
{% if generatorConfiguration.collectErrors() %}
$originalErrorRegistry = $this->_errorRegistry;
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/Templates/Validator/ArrayItem.phptpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
is_array($value) && (function (&$items) use (&$invalidItems{{ suffix }}) {
is_array($value) && (function (&$items) use (&$invalidItems{{ suffix }}, $modelData) {
{% if generatorConfiguration.collectErrors() %}
$originalErrorRegistry = $this->_errorRegistry;
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/Templates/Validator/ArrayTuple.phptpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
is_array($value) && (function (&$items) use (&$invalidTuples) {
is_array($value) && (function (&$items) use (&$invalidTuples, $modelData) {
{% if generatorConfiguration.collectErrors() %}
$originalErrorRegistry = $this->_errorRegistry;
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/Templates/Validator/PatternProperties.phptpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(function () use ($properties, &$invalidProperties) {
(function () use ($properties, &$invalidProperties, $modelData) {
{% if generatorConfiguration.collectErrors() %}
$originalErrorRegistry = $this->_errorRegistry;
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/Utils/RenderHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public function renderValidator(PropertyValidatorInterface $validator, Schema $s
$schema->addMethod($validator->getExtractedMethodName(), $validator->getMethod());
}

return "\$this->{$validator->getExtractedMethodName()}(\$value);";
return "\$this->{$validator->getExtractedMethodName()}(\$value, \$modelData);";
}

public function renderMethods(Schema $schema): string
Expand Down
207 changes: 201 additions & 6 deletions tests/Objects/ConstPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

namespace PHPModelGenerator\Tests\Objects;

use PHPModelGenerator\Exception\Arrays\InvalidTupleException;
use PHPModelGenerator\Exception\ComposedValue\OneOfException;
use PHPModelGenerator\Exception\ErrorRegistryException;
use PHPModelGenerator\Exception\FileSystemException;
use PHPModelGenerator\Exception\Object\RequiredValueException;
use PHPModelGenerator\Exception\ValidationException;
use PHPModelGenerator\Exception\RenderException;
use PHPModelGenerator\Exception\SchemaException;
use PHPModelGenerator\Model\GeneratorConfiguration;
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTestCase;
use stdClass;

Expand Down Expand Up @@ -38,14 +43,38 @@ public function testProvidedConstPropertyIsValid(): void
* @throws RenderException
* @throws SchemaException
*/
public function testNotProvidedConstPropertyThrowsAnException(): void
public function testProvidedArrayItemConstPropertyIsValid(): void
{
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('Invalid value for stringProperty declined by const constraint');
$className = $this->generateClassFromFile('ArrayItemConstProperty.json');

$className = $this->generateClassFromFile('ConstProperty.json');
$object = new $className(['property' => ['red', 'red']]);

new $className([]);
$this->assertIsArray($object->getProperty());
$this->assertSame(['red', 'red'], $object->getProperty());
}

/**
* @dataProvider stringIntDataProvider
*
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testProvidedAnyOfConstPropertyIsValid(string|int $propertyValue): void
{
$className = $this->generateClassFromFile('AnyOfConstProperty.json');

$object = new $className(['property' => $propertyValue]);

$this->assertSame($propertyValue, $object->getProperty());
}

public function stringIntDataProvider(): array
{
return [
['red'],
[1],
];
}

/**
Expand All @@ -62,7 +91,7 @@ public function testNotMatchingProvidedDataThrowsAnException($propertyValue): vo
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('Invalid value for stringProperty declined by const constraint');

$className = $this->generateClassFromFile('ConstProperty.json');
$className = $this->generateClassFromFile('ConstProperty.json', null, false, false);

new $className(['stringProperty' => $propertyValue]);
}
Expand All @@ -79,4 +108,170 @@ public function invalidPropertyDataProvider(): array
'null' => [null],
];
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testNotMatchingArrayItemConstPropertyThrowsAnException(): void
{
$this->expectException(InvalidTupleException::class);
$this->expectExceptionMessage('Invalid tuple item in array property');

$className = $this->generateClassFromFile('ArrayItemConstProperty.json');

new $className(['property' => ['green']]);
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testNotMatchingArrayItemConstPropertyThrowsAnException1(): void
{
$this->expectException(OneOfException::class);
$this->expectExceptionMessage('Invalid value for property declined by composition constraint');

$className = $this->generateClassFromFile('AnyOfConstProperty.json');

new $className(['property' => 'green']);
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testProvidedConstOnlyRequiredPropertyIsValid(): void
{
$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');

$object = new $className(['requiredProperty' => 'red']);

$this->assertSame('red', $object->getRequiredProperty());
$this->assertNull($object->getOptionalProperty());
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testProvidedNullOptionalPropertyConstPropertyIsValid(): void
{
$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');

$object = new $className(['requiredProperty' => 'red', 'optionalProperty' => null]);

$this->assertSame('red', $object->getRequiredProperty());
$this->assertNull($object->getOptionalProperty());
}

/**
* @dataProvider requiredAndOptionalPropertiesDataProvider
*
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testProvidedConstPropertiesIsValidWithDifferentImplicitNull(
bool $implicitNull,
string $reqPropertyValue,
string $optPropertyValue
): void
{
$className = $this->generateClassFromFile(
'RequiredAndOptionalConstProperties.json',
new GeneratorConfiguration(),
false,
$implicitNull,
);

$object = new $className(['requiredProperty' => $reqPropertyValue, 'optionalProperty' => $optPropertyValue]);

$this->assertSame($reqPropertyValue, $object->getRequiredProperty());
$this->assertSame($optPropertyValue, $object->getOptionalProperty());
}

public function requiredAndOptionalPropertiesDataProvider(): array
{
return $this->combineDataProvider(
$this->implicitNullDataProvider(),
[
['red', 'green'],
],
);
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testNotProvidedRequiredPropertyThrowsAnException(): void
{
$this->expectException(RequiredValueException::class);
$this->expectExceptionMessage('Missing required value for requiredProperty');

$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');

new $className([]);
}

/**
* @dataProvider invalidRequiredAndOptionalConstPropertiesDataProvider
*
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testNotMatchingRequiredAndOptionalProvidedDataThrowsAnException(
bool $implicitNull,
string $reqPropertyValue,
?string $optPropertyValue,
string $exceptionMessage
): void
{
$className = $this->generateClassFromFile(
'RequiredAndOptionalConstProperties.json',
new GeneratorConfiguration(),
false,
$implicitNull,
);

$this->expectException(ErrorRegistryException::class);
$this->expectExceptionMessage($exceptionMessage);

new $className(['requiredProperty' => $reqPropertyValue, 'optionalProperty' => $optPropertyValue]);
}

public function invalidRequiredAndOptionalConstPropertiesDataProvider(): array
{
return $this->combineDataProvider(
$this->implicitNullDataProvider(),
[
['blue', 'green', 'Invalid value for requiredProperty declined by const constraint'],
['blue', null, 'Invalid value for requiredProperty declined by const constraint'],
['red', 'blue', 'Invalid value for optionalProperty declined by const constraint'],
['red', '0', 'Invalid value for optionalProperty declined by const constraint'],
['red', '', 'Invalid value for optionalProperty declined by const constraint'],
],
);
}

/**
* @throws FileSystemException
* @throws RenderException
* @throws SchemaException
*/
public function testProvidedNullValueConstPropertyIsValid(): void
{
$className = $this->generateClassFromFile('NullValueConstProperty.json', null, false, false);

$object = new $className(['nullProperty' => null]);

$this->assertNull($object->getNullProperty());
}
}
15 changes: 15 additions & 0 deletions tests/Schema/ConstPropertyTest/AnyOfConstProperty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "object",
"properties": {
"property": {
"oneOf": [
{
"const": "red"
},
{
"const": 1
}
]
}
}
}
13 changes: 13 additions & 0 deletions tests/Schema/ConstPropertyTest/ArrayItemConstProperty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "object",
"properties": {
"property": {
"type": "array",
"items": [
{
"const": "red"
}
]
}
}
}
11 changes: 11 additions & 0 deletions tests/Schema/ConstPropertyTest/NullValueConstProperty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "object",
"properties": {
"nullProperty": {
"const": null
}
},
"required": [
"nullProperty"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"type": "object",
"properties": {
"requiredProperty": {
"const": "red"
},
"optionalProperty": {
"const": "green"
}
},
"required": [
"requiredProperty"
]
}

0 comments on commit 83acf61

Please sign in to comment.