Skip to content

Commit

Permalink
Merge pull request #5 from rey26/4-support-symfony-71
Browse files Browse the repository at this point in the history
Support Symfony 7.1
  • Loading branch information
Prometee authored Jun 6, 2024
2 parents 4cb2ccb + f626fdb commit b9cf778
Show file tree
Hide file tree
Showing 22 changed files with 308 additions and 172 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ jobs:
fail-fast: false
matrix:
odoo: [13, 14, 15, 16, 17]
symfony: ["^5.4", "^6.4"]
symfony: ["^6.4", "^7.1"]
php: ["8.1", "8.2", "8.3"] # " required to keep the .0
exclude:
-
symfony: ^7.1
php: "8.1"
services:
postgres:
image: postgres
Expand Down Expand Up @@ -147,9 +151,6 @@ jobs:
-
name: Run PHPStan
run: vendor/bin/phpstan analyse -l max src/ tests/
-
name: Run Psalm
run: vendor/bin/psalm --show-info=false --output-format=github --php-version=${{ matrix.php }}
-
name: Run PHPUnit
run: vendor/bin/phpunit --colors=always
Expand Down
18 changes: 9 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
"php-http/client-common": "^2.1",
"php-http/discovery": "^1.7",
"psr/http-factory-implementation": "^1.0",
"symfony/console": "^5.4|^6.4",
"symfony/serializer": "^5.4|^6.4",
"symfony/property-access": "^5.4|^6.4",
"symfony/property-info": "^5.4|^6.4",
"symfony/string": "^5.4|^6.4",
"symfony/http-kernel": "^5.4|^6.4",
"symfony/console": "^6.4|^7.1",
"symfony/serializer": "^6.4|^7.1",
"symfony/property-access": "^6.4|^7.1",
"symfony/property-info": "^6.4|^7.1",
"symfony/string": "^6.4|^7.1",
"symfony/http-kernel": "^6.4|^7.1",
"php-http/logger-plugin": "^1.1",
"phpdocumentor/reflection-docblock": "^5.4",
"prometee/php-class-generator": "^1.0",
"doctrine/annotations": "^1.13|^2.0",
"webmozart/assert": "^1"
},
"require-dev": {
Expand All @@ -49,7 +49,6 @@
"http-interop/http-factory-guzzle": "^1.0",
"symplify/easy-coding-standard": "^12",
"phpstan/phpstan": "^1",
"vimeo/psalm": "^5",
"rector/rector": "^1.0.4"
},
"suggest": {
Expand All @@ -60,7 +59,8 @@
"bin/odoo-model-classes-generator"
],
"autoload": {
"psr-4": { "FluxSE\\OdooApiClient\\": "src/" }
"psr-4": { "FluxSE\\OdooApiClient\\": "src/" },
"files": ["src/functions.php"]
},
"autoload-dev": {
"psr-4": { "Tests\\FluxSE\\OdooApiClient\\": "tests/" }
Expand Down
16 changes: 7 additions & 9 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,16 @@ parameters:
- identifier: missingType.iterableValue
- '/Method FluxSE\\OdooApiClient\\Manager\\ModelListManager::(find|findBy|findByIds)\(\) should return (array<|\()T of FluxSE\\OdooApiClient\\Model\\BaseInterface(>|\)\|null) but returns mixed\./'
- '/Method FluxSE\\OdooApiClient\\Serializer\\(Json|Xml)Rpc\\(Json|Xml)RpcSerializerHelper::decodeResponseBody\(\) should return array\|bool\|int\|string but returns mixed\./'

-
message: '/Class FluxSE\\OdooApiClient\\Serializer\\OdooNormalizer extends \@final class Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer\./'
message: '/Method FluxSE\\OdooApiClient\\Serializer\\OdooNormalizer::normalize\(\) return type with generic class ArrayObject does not specify its types: TKey, TValue/'
count: 1
path: src/Serializer/OdooNormalizer.php
# Symfony 6.4
-
message: '/Parameter #1 \$onFulfilled of method Http\\Promise\\Promise<mixed>::then\(\) expects \(callable\(mixed\): Psr\\Http\\Message\\ResponseInterface\)\|null, Closure\(Psr\\Http\\Message\\ResponseInterface\): Psr\\Http\\Message\\ResponseInterface given\./'
message: '/Method Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer::supportsDenormalization\(\) invoked with 4 parameters, 2-3 required\./'
count: 1
path: src/HttPlug/Plugin/OdooApiErrorPlugin.php
# Symfony 5.4
- '/Method FluxSE\\OdooApiClient\\Serializer\\(Json|Xml)Rpc\\(Json|Xml)RpcSerializerHelper::deserializeResponseBody\(\) should return T of object but returns mixed\./'
path: src/Serializer/OdooNormalizer.php
-
message: '/Method FluxSE\\OdooApiClient\\HttPlug\\Plugin\\OdooApiErrorPlugin::handleRequest\(\) has parameter \$(first|next) with generic interface Http\\Promise\\Promise but does not specify its types: T/'
count: 2
path: src/HttPlug/Plugin/OdooApiErrorPlugin.php
message: '/Method Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer::supportsNormalization\(\) invoked with 3 parameters, 1-2 required\./'
count: 1
path: src/Serializer/OdooNormalizer.php
37 changes: 0 additions & 37 deletions psalm.xml

This file was deleted.

4 changes: 2 additions & 2 deletions src/Manager/ModelManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use FluxSE\OdooApiClient\Model\BaseInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\OptionsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\RecordOperationsInterface;
use FluxSE\OdooApiClient\Serializer\OdooNormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationsNormalizer;
use LogicException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

Expand Down Expand Up @@ -38,7 +38,7 @@ public function update(BaseInterface $model, ?OptionsInterface $options = null):
}

$normalizedModel = (array) $this->normalizer->normalize($model, null, [
OdooNormalizer::NORMALIZE_FOR_UPDATE => true,
OdooRelationsNormalizer::NORMALIZE_FOR_UPDATE => true,
]);
return $this->recordOperations->write(
$model::getOdooModelName(),
Expand Down
73 changes: 73 additions & 0 deletions src/PropertyAccess/OdooPropertyAccessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace FluxSE\OdooApiClient\PropertyAccess;

use FluxSE\OdooApiClient\Model\OdooRelation;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyAccess\PropertyPathInterface;

final class OdooPropertyAccessor implements PropertyAccessorInterface
{
public function __construct(private PropertyAccessorInterface $decoratedPropertyAccessor)
{
}

public function setValue(object|array &$objectOrArray, PropertyPathInterface|string $propertyPath, mixed $value): void
{
/**
* Override to set null when original value is false
*
* Because null value is transformed to false by Odoo API
* There is no other way to do it simply with any features given by Symfony Serializer
*/

$this->decoratedPropertyAccessor->setValue($objectOrArray, $propertyPath, $value);

if (false !== $value) {
return;
}

$newValue = $this->decoratedPropertyAccessor->getValue($objectOrArray, $propertyPath);

if (false === $newValue) {
return;
}

// set null instead of false
$this->decoratedPropertyAccessor->setValue($objectOrArray, $propertyPath, null);
}

public function getValue(object|array $objectOrArray, PropertyPathInterface|string $propertyPath): mixed
{
/**
* Specific case of normalized data returned as null instead of being just transformed
*/
$value = $this->decoratedPropertyAccessor->getValue($objectOrArray, $propertyPath);

if (false === $value instanceof OdooRelation) {
return $value;
}

if (null !== $value->getCommand()) {
return $value;
}

if (null !== $value->getId()) {
return $value;
}

return null;
}

public function isWritable(object|array $objectOrArray, PropertyPathInterface|string $propertyPath): bool
{
return $this->decoratedPropertyAccessor->isWritable($objectOrArray, $propertyPath);
}

public function isReadable(object|array $objectOrArray, PropertyPathInterface|string $propertyPath): bool
{
return $this->decoratedPropertyAccessor->isReadable($objectOrArray, $propertyPath);
}
}
89 changes: 46 additions & 43 deletions src/Serializer/Factory/SerializerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace FluxSE\OdooApiClient\Serializer\Factory;

use Doctrine\Common\Annotations\AnnotationReader;
use FluxSE\OdooApiClient\PropertyAccess\OdooPropertyAccessor;
use FluxSE\OdooApiClient\Serializer\JsonRpc\JsonRpcDecoder;
use FluxSE\OdooApiClient\Serializer\JsonRpc\JsonRpcEncoder;
use FluxSE\OdooApiClient\Serializer\NullableDateTimeDenormalizer;
Expand All @@ -17,12 +17,13 @@
use FluxSE\OdooApiClient\Serializer\OdooRelationsNormalizer;
use FluxSE\OdooApiClient\Serializer\XmlRpc\XmlRpcDecoder;
use FluxSE\OdooApiClient\Serializer\XmlRpc\XmlRpcEncoder;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
Expand All @@ -31,6 +32,7 @@
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

final class SerializerFactory implements SerializerFactoryInterface
Expand Down Expand Up @@ -82,60 +84,61 @@ public function setupEncoders(): array

public function setupObjectNormalizer(): OdooNormalizer
{
if (class_exists(AttributeLoader::class)) {
/** @var LoaderInterface $loader */
$loader = new AttributeLoader();
} else {
/**
* @var LoaderInterface $loader
* @psalm-suppress TooManyArguments
*/
$loader = new AnnotationLoader(new AnnotationReader());
}
$loader = new AttributeLoader();

$classMetadataFactory = new ClassMetadataFactory($loader);
$metadataAwareNameConverter = new MetadataAwareNameConverter(
$classMetadataFactory,
new CamelCaseToSnakeCaseNameConverter()
);

$odooPropertyAccessor = $this->setupPropertyAccessor();

return new OdooNormalizer(
$classMetadataFactory,
$metadataAwareNameConverter,
null,
new PropertyInfoExtractor(
[
new ReflectionExtractor(),
new SerializerExtractor($classMetadataFactory),
],
[
new PhpDocExtractor(),
new ReflectionExtractor(),
],
[
new PhpDocExtractor(),
],
new ObjectNormalizer(
$classMetadataFactory,
$metadataAwareNameConverter,
$odooPropertyAccessor,
new PropertyInfoExtractor(
[
new ReflectionExtractor(),
new SerializerExtractor($classMetadataFactory),
],
[
new PhpDocExtractor(),
new ReflectionExtractor(),
],
[
new PhpDocExtractor(),
],
[
new ReflectionExtractor(),
],
[
new ReflectionExtractor(),
]
),
null,
null,
[
new ReflectionExtractor(),
],
[
new ReflectionExtractor(),
// => array to model
AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true,
// => model to array
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) {
return $object->getId() ?? 0;
}
]
),
null,
null,
[
// => array to model
AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true,
// => model to array
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object) {
return $object->getId() ?? 0;
}
]
)
);
}

public function setupPropertyAccessor(): PropertyAccessorInterface
{
$propertyAccessor = PropertyAccess::createPropertyAccessor();
return new OdooPropertyAccessor($propertyAccessor);
}

public function getDateFormat(): string
{
return $this->dateFormat;
Expand Down
6 changes: 6 additions & 0 deletions src/Serializer/Factory/SerializerFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace FluxSE\OdooApiClient\Serializer\Factory;

use FluxSE\OdooApiClient\Serializer\OdooNormalizer;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Serializer\Serializer;

interface SerializerFactoryInterface
Expand All @@ -13,4 +15,8 @@ public function create(): Serializer;
public function setupNormalizers(): array;

public function setupEncoders(): array;

public function setupObjectNormalizer(): OdooNormalizer;

public function setupPropertyAccessor(): PropertyAccessorInterface;
}
2 changes: 1 addition & 1 deletion src/Serializer/JsonRpc/JsonRpcDecoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function supportsDecoding($format, array $context = []): bool
*
* @throws JsonException
*/
public function decode($data, $format, array $context = [])
public function decode(string $data, string $format, array $context = []): mixed
{
if ('' === trim($data)) {
throw new UnexpectedValueException('Invalid JSON data, it can not be empty.');
Expand Down
Loading

0 comments on commit b9cf778

Please sign in to comment.