Skip to content

Commit

Permalink
Refactor to use the new getSupportedTypes on the serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Prometee committed May 8, 2024
1 parent 25d9dec commit 79ae794
Show file tree
Hide file tree
Showing 23 changed files with 344 additions and 82 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,21 @@ jobs:
run: |
bin/odoo-model-classes-generator -vvv \
"./tests/TestModel/Object" \
"Tests\\FluxSE\\OdooApiClient\\TestModel\\Object"
"Tests\\FluxSE\\OdooApiClient\\TestModel\\Object" \
--only-model=account.account \
--only-model=account.journal \
--only-model=account.move \
--only-model=account.move.line \
--only-model=account.payment \
--only-model=account.payment.method \
--only-model=account.payment.register \
--only-model=account.tax \
--only-model=product.category \
--only-model=product.product \
--only-model=product.template \
--only-model=res.currency \
--only-model=res.partner \
--only-model=uom.uom
# Need classes from Odoo to be generated for the tests folder
-
Expand Down
23 changes: 5 additions & 18 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,20 @@ parameters:
- tests/Manager/ModelManagerTest.php

ignoreErrors:
- '/Method FluxSE\\OdooApiClient\\Manager\\ModelListManager::(find|findBy)\(\) should return (array<|\()T of FluxSE\\OdooApiClient\\Model\\BaseInterface(>|\)\|null) but returns mixed\./'
- '/Method FluxSE\\OdooApiClient\\Serializer\\(Json|Xml)Rpc\\(Json|Xml)RpcSerializerHelper::deserializeResponseBody\(\) should return T of object but returns mixed\./'
- '/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\./'
count: 1
path: src/Serializer/OdooNormalizer.php
-
message: '/Method FluxSE\\OdooApiClient\\Serializer\\OdooNormalizer::setAttributeValue\(\) has parameter \$value with no type specified\./'
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\./'
count: 1
path: src/Serializer/OdooNormalizer.php
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\./'
-
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: '/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\./'
count: 1
path: src/HttPlug/Plugin/OdooApiErrorPlugin.php
# Odoo v17
# https://github.com/odoo/odoo/issues/144841
-
message: '/Method Tests\\FluxSE\\OdooApiClient\\TestModel\\Object\\Mail\\Alias::__construct\(\) invoked with 1 parameter, 3 required\./'
count: 1
path: tests/TestModel/Object/Mail/Alias/Mixin.php
-
message: '/Parameter #1 \$alias_model_id of method Tests\\FluxSE\\OdooApiClient\\TestModel\\Object\\Mail\\Alias::__construct\(\) expects FluxSE\\OdooApiClient\\Model\\OdooRelation, string given\./'
count: 1
path: tests/TestModel/Object/Mail/Alias/Mixin.php
10 changes: 0 additions & 10 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,5 @@
<file name="src/Serializer/OdooNormalizer.php" />
</errorLevel>
</MethodSignatureMismatch>
<TooFewArguments>
<errorLevel type="info">
<file name="tests/TestModel/Object/Mail/Alias/Mixin.php" />
</errorLevel>
</TooFewArguments>
<InvalidArgument>
<errorLevel type="info">
<file name="tests/TestModel/Object/Mail/Alias/Mixin.php" />
</errorLevel>
</InvalidArgument>
</issueHandlers>
</psalm>
1 change: 0 additions & 1 deletion src/Api/Fault.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public function __construct(private int $faultCode, private string $faultString)
{
}


public function getFaultCode(): int
{
return $this->faultCode;
Expand Down
4 changes: 2 additions & 2 deletions src/Command/GeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ protected function configure(): void
->addArgument(
'path',
InputArgument::REQUIRED,
'The path where classes will be generated (ex: ./src/OdooModel/Object)'
'The path where classes will be generated (ex: ./src/Odoo/Model/Object)'
)
->addArgument(
'namespace',
InputArgument::REQUIRED,
'The base namespace of the generated classes (ex: "App\\OdooModel\\Object")'
'The base namespace of the generated classes (ex: "App\\Odoo\Model\\Object")'
)
->addOption(
'host',
Expand Down
3 changes: 0 additions & 3 deletions src/HttPlug/Plugin/OdooApiErrorPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public function __construct(
) {
}

/**
* @return Promise<ResponseInterface>
*/
public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
{
$errorNext = function (RequestInterface $request) use ($next, $first): Promise {
Expand Down
67 changes: 55 additions & 12 deletions src/Manager/ModelListManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use FluxSE\OdooApiClient\Model\BaseInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Arguments\SearchDomainsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\ReadOptions;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\ReadOptionsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\SearchReadOptions;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\SearchReadOptionsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\RecordListOperationsInterface;
Expand All @@ -21,26 +23,46 @@ public function __construct(
) {
}

public function find(string $className, int $id): ?BaseInterface
{
$modelName = $this->getModelNameFromClass($className);

$results = $this->recordListOperations->read($modelName, [$id]);
public function find(
string $className,
int $id,
?ReadOptionsInterface $readOptions = null
): ?BaseInterface {
$results = $this->findByIds($className, [$id], $readOptions);

if (count($results) === 0) {
return null;
}

return $this->serializer->denormalize($results[0], $className);
return $results[0];
}

public function findOneBy(string $className, ?SearchDomainsInterface $searchDomains = null): ?BaseInterface
{
$searchReadOptions = new SearchReadOptions();
public function findByIds(
string $className,
array $ids,
?ReadOptionsInterface $readOptions = null
): array {
$readOptions = $readOptions ?? new ReadOptions();

$this->processReadOptions($className, $readOptions, [
'class' => __CLASS__,
'method' => __METHOD__,
]);

$modelName = $this->getModelNameFromClass($className);

$results = $this->recordListOperations->read($modelName, $ids, $readOptions);

return $this->serializer->denormalize($results, sprintf('%s[]', $className));
}

public function findOneBy(
string $className,
?SearchDomainsInterface $searchDomains = null,
?SearchReadOptionsInterface $searchReadOptions = null
): ?BaseInterface {
$searchReadOptions = $searchReadOptions ?? new SearchReadOptions();
$searchReadOptions->setLimit(1);
$searchReadOptions->setFields($this->modelFieldsProvider->provide($className, [
'searchDomains' => $searchDomains,
]));

$results = $this->findBy($className, $searchDomains, $searchReadOptions);

Expand All @@ -58,6 +80,13 @@ public function findBy(
): array {
$modelName = $this->getModelNameFromClass($className);

$searchReadOptions = $searchReadOptions ?? new SearchReadOptions();

$this->processReadOptions($className, $searchReadOptions, [
'class' => __CLASS__,
'method' => __METHOD__,
]);

$results = $this->recordListOperations->search_read(
$modelName,
$searchDomains,
Expand Down Expand Up @@ -94,4 +123,18 @@ public function getSerializer(): Serializer
{
return $this->serializer;
}

/**
* @template T of BaseInterface
* @param class-string<T> $className
*/
private function processReadOptions(
string $className,
ReadOptionsInterface $readOptions,
array $context = []
): void {
$readOptions->setFields($this->modelFieldsProvider->provide($className, $context + [
ModelFieldsProviderInterface::FIELDS_CONTEXT => $readOptions->getFields(),
]));
}
}
29 changes: 25 additions & 4 deletions src/Manager/ModelListManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use FluxSE\OdooApiClient\Model\BaseInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Arguments\SearchDomainsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\ReadOptionsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\SearchReadOptionsInterface;
use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\RecordListOperationsInterface;
use Symfony\Component\Serializer\Serializer;
Expand All @@ -17,23 +18,43 @@ interface ModelListManagerInterface
* @param class-string<T> $className
* @return T|null
*/
public function find(string $className, int $id): ?BaseInterface;
public function find(
string $className,
int $id,
?ReadOptionsInterface $readOptions = null
): ?BaseInterface;

/**
* @template T of BaseInterface
* @param class-string<T> $className
* @param int[] $ids
* @return T[]
*/
public function findByIds(
string $className,
array $ids,
?ReadOptionsInterface $readOptions = null
): array;

/**
* @template T of BaseInterface
* @param class-string<T> $className
* @return T|null
*/
public function findOneBy(string $className, ?SearchDomainsInterface $searchDomains = null): ?BaseInterface;
public function findOneBy(
string $className,
?SearchDomainsInterface $searchDomains = null,
?SearchReadOptionsInterface $searchReadOptions = null
): ?BaseInterface;

/**
* @template T of BaseInterface
* @param class-string<T> $className
* @return T[]
*/
public function findBy(
string $className,
?SearchDomainsInterface $searchDomains = null,
string $className,
?SearchDomainsInterface $searchDomains = null,
?SearchReadOptionsInterface $searchReadOptions = null
): array;

Expand Down
17 changes: 16 additions & 1 deletion src/Provider/ModelFieldsProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,31 @@

namespace FluxSE\OdooApiClient\Provider;

use FluxSE\OdooApiClient\Operations\Object\ExecuteKw\Options\ReadOptionsInterface;
use ReflectionClass;
use Webmozart\Assert\Assert;

final class ModelFieldsProvider implements ModelFieldsProviderInterface
{
public function provide(string $className, array $context): array
{
$fields = [];
if (isset($context[self::FIELDS_CONTEXT])) {
/** @var string[] $fields */
$fields = $context[self::FIELDS_CONTEXT];

Assert::isArray($fields, 'The field context should be an array');
}

$reflectionClass = new ReflectionClass($className);
foreach ($reflectionClass->getProperties() as $property) {
$fields[] = $property->getName();
$propertyName = $property->getName();

if (in_array($propertyName, $fields, true)) {
continue;
}

$fields[] = $propertyName;
}

return $fields;
Expand Down
2 changes: 2 additions & 0 deletions src/Provider/ModelFieldsProviderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

interface ModelFieldsProviderInterface
{
public const FIELDS_CONTEXT = 'read_options';

/**
* @template T of BaseInterface
* @param class-string<T> $className
Expand Down
2 changes: 2 additions & 0 deletions src/Serializer/Factory/SerializerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use FluxSE\OdooApiClient\Serializer\OdooNormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationDenormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationNormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationsDenormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationSingleDenormalizer;
use FluxSE\OdooApiClient\Serializer\OdooRelationsNormalizer;
use FluxSE\OdooApiClient\Serializer\XmlRpc\XmlRpcDecoder;
Expand Down Expand Up @@ -57,6 +58,7 @@ public function setupNormalizers(): array
]);
return [
new ArrayDenormalizer(),
new OdooRelationsDenormalizer(),
new NullableDateTimeDenormalizer($dateTimeNormalizer),
$dateTimeNormalizer,
new OdooRelationsNormalizer(),
Expand Down
8 changes: 8 additions & 0 deletions src/Serializer/NullOdooRelationDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

/**
* false ==> null
*/
final class NullOdooRelationDenormalizer implements DenormalizerInterface
{
public function getSupportedTypes(?string $format): array
{
return [OdooRelation::class => false];
}

public function supportsDenormalization($data, $type, $format = null): bool
{
if ($type !== OdooRelation::class) {
Expand Down
9 changes: 9 additions & 0 deletions src/Serializer/NullableDateTimeDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ public function __construct(private DenormalizerInterface $dateTimeNormalizer)
{
}

public function getSupportedTypes(?string $format): array
{
return [
\DateTimeInterface::class => false,
\DateTimeImmutable::class => false,
\DateTime::class => false,
];
}

public function denormalize($data, string $type, string $format = null, array $context = [])
{
return null;
Expand Down
14 changes: 14 additions & 0 deletions src/Serializer/OdooNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

namespace FluxSE\OdooApiClient\Serializer;

use FluxSE\OdooApiClient\Api\FaultInterface;
use FluxSE\OdooApiClient\Api\RequestBodyInterface;
use FluxSE\OdooApiClient\Model\BaseInterface;
use FluxSE\OdooApiClient\Model\Common\Version;
use FluxSE\OdooApiClient\Model\OdooRelation;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
Expand All @@ -12,6 +16,16 @@ final class OdooNormalizer extends ObjectNormalizer
{
public const NORMALIZE_FOR_UPDATE = 'normalize_for_update';

public function getSupportedTypes(?string $format): array
{
return [
BaseInterface::class => true,
RequestBodyInterface::class => true,
FaultInterface::class => true,
Version::class => true,
];
}

protected function setAttributeValue(object $object, string $attribute, mixed $value, string $format = null, array $context = []): void
{
/**
Expand Down
Loading

0 comments on commit 79ae794

Please sign in to comment.