Skip to content

Commit

Permalink
Fix FiasSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
liquetsoft committed Sep 24, 2024
1 parent 9b52b58 commit 7027095
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 51 deletions.
82 changes: 54 additions & 28 deletions src/Serializer/CompiledEntitesDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,40 @@
/**
* Скомпилированный класс для денормализации сущностей ФИАС в модели eloquent.
*/
final class CompiledEntitesDenormalizer implements DenormalizerInterface
class CompiledEntitesDenormalizer implements DenormalizerInterface
{
private const ALLOWED_ENTITIES = [
Apartments::class => true,
AddrObjDivision::class => true,
NormativeDocsTypes::class => true,
RoomTypes::class => true,
ObjectLevels::class => true,
NormativeDocsKinds::class => true,
Rooms::class => true,
ApartmentTypes::class => true,
AddrObjTypes::class => true,
Steads::class => true,
NormativeDocs::class => true,
OperationTypes::class => true,
Houses::class => true,
AdmHierarchy::class => true,
Carplaces::class => true,
ChangeHistory::class => true,
AddrObj::class => true,
ParamTypes::class => true,
Param::class => true,
ReestrObjects::class => true,
HouseTypes::class => true,
MunHierarchy::class => true,
FiasVersion::class => true,
Apartments::class,
AddrObjDivision::class,
NormativeDocsTypes::class,
RoomTypes::class,
ObjectLevels::class,
NormativeDocsKinds::class,
Rooms::class,
ApartmentTypes::class,
AddrObjTypes::class,
Steads::class,
NormativeDocs::class,
OperationTypes::class,
Houses::class,
AdmHierarchy::class,
Carplaces::class,
ChangeHistory::class,
AddrObj::class,
ParamTypes::class,
Param::class,
ReestrObjects::class,
HouseTypes::class,
MunHierarchy::class,
FiasVersion::class,
];

/**
* {@inheritDoc}
*/
public function supportsDenormalization($data, string $type, ?string $format = null)
{
return \array_key_exists(trim($type, " \t\n\r\0\x0B\\/"), self::ALLOWED_ENTITIES);
return \in_array(trim($type, " \t\n\r\0\x0B\\/"), self::ALLOWED_ENTITIES);
}

/**
Expand All @@ -84,7 +84,8 @@ public function denormalize($data, string $type, ?string $format = null, array $
$entity = $context[AbstractNormalizer::OBJECT_TO_POPULATE] ?? new $type();

if (!($entity instanceof Model)) {
throw new InvalidArgumentException("Bad class for populating entity, '" . Model::class . "' is required");
$message = \sprintf("Bad class for populating entity, need '%s' instance.", Model::class);
throw new InvalidArgumentException($message);
}

switch ($type) {
Expand Down Expand Up @@ -158,7 +159,8 @@ public function denormalize($data, string $type, ?string $format = null, array $
$extractedData = $this->modelFiasVersionDataExtractor($data);
break;
default:
throw new InvalidArgumentException("Can't find data extractor for '{$type}' type");
$message = \sprintf("Can't find data extractor for '%s' type.", $type);
throw new InvalidArgumentException($message);
break;
}

Expand All @@ -172,7 +174,31 @@ public function denormalize($data, string $type, ?string $format = null, array $
*/
public function getSupportedTypes(?string $format): array
{
return self::ALLOWED_ENTITIES;
return [
Apartments::class => true,
AddrObjDivision::class => true,
NormativeDocsTypes::class => true,
RoomTypes::class => true,
ObjectLevels::class => true,
NormativeDocsKinds::class => true,
Rooms::class => true,
ApartmentTypes::class => true,
AddrObjTypes::class => true,
Steads::class => true,
NormativeDocs::class => true,
OperationTypes::class => true,
Houses::class => true,
AdmHierarchy::class => true,
Carplaces::class => true,
ChangeHistory::class => true,
AddrObj::class => true,
ParamTypes::class => true,
Param::class => true,
ReestrObjects::class => true,
HouseTypes::class => true,
MunHierarchy::class => true,
FiasVersion::class => true,
];
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Serializer/EloquentDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function __construct(?TypeCaster $typeCaster = null)
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
public function supportsDenormalization($data, string $type, ?string $format = null)
{
return is_subclass_of($type, Model::class);
}
Expand All @@ -38,7 +38,7 @@ public function supportsDenormalization($data, $type, $format = null)
*
* @psalm-suppress InvalidStringClass
*/
public function denormalize($data, $type, $format = null, array $context = [])
public function denormalize($data, string $type, ?string $format = null, array $context = [])
{
$data = \is_array($data) ? $data : [];
$type = trim($type, " \t\n\r\0\x0B\\/");
Expand Down
3 changes: 2 additions & 1 deletion src/Serializer/FiasSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* Преднастроенный объект сериализатора для ФИАС.
*/
class FiasSerializer extends Serializer
final class FiasSerializer extends Serializer
{
/**
* @param array<DenormalizerInterface|NormalizerInterface>|null $normalizers
Expand All @@ -28,6 +28,7 @@ public function __construct(?array $normalizers = null, ?array $encoders = null)
if ($normalizers === null) {
$normalizers = [
new CompiledEntitesDenormalizer(),
new EloquentDenormalizer(),
new ObjectNormalizer(
null,
new FiasNameConverter(),
Expand Down
167 changes: 167 additions & 0 deletions tests/Serializer/EloquentDenormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php

declare(strict_types=1);

namespace Liquetsoft\Fias\Laravel\LiquetsoftFiasBundle\Tests\Serializer;

use Illuminate\Database\Eloquent\Model;
use Liquetsoft\Fias\Laravel\LiquetsoftFiasBundle\Serializer\EloquentDenormalizer;
use Liquetsoft\Fias\Laravel\LiquetsoftFiasBundle\Serializer\TypeCaster\TypeCaster;
use Liquetsoft\Fias\Laravel\LiquetsoftFiasBundle\Tests\BaseCase;
use Liquetsoft\Fias\Laravel\LiquetsoftFiasBundle\Tests\MockModel\FiasSerializerMock;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;

/**
* @internal
*/
final class EloquentDenormalizerTest extends BaseCase
{
/**
* Проверяет, что денормалайзер правильно определит, что может преобразовать тип.
*
* @dataProvider provideSupportsDenormalization
*/
public function testSupportsDenormalization(string $type, bool $expected): void
{
$caster = $this->mock(TypeCaster::class);

$denormalizer = new EloquentDenormalizer($caster);
$res = $denormalizer->supportsDenormalization([], $type);

$this->assertSame($expected, $res);
}

public static function provideSupportsDenormalization(): array
{
return [
'supported type' => [
FiasSerializerMock::class,
true,
],
'unsupported type' => [
'test',
false,
],
];
}

/**
* Проверяет, что денормалайзер правильно преобразует массив в модель.
*/
public function testDenormalize(): void
{
$data = [
'actstatid' => '100',
'@name' => 'test',
'FLOATNUM' => '10.2',
'boolVal' => '1',
'timestamp' => 120000,
'nullableCast' => null,
'wrong_attr' => 'wrong attr value',
];

$denormalizer = new EloquentDenormalizer();
$res = $denormalizer->denormalize($data, FiasSerializerMock::class);

$this->assertInstanceOf(FiasSerializerMock::class, $res);
$this->assertSame((int) $data['actstatid'], $res->getAttribute('ACTSTATID'));
$this->assertSame($data['@name'], $res->getAttribute('name'));
$this->assertSame((float) $data['FLOATNUM'], $res->getAttribute('floatNum'));
$this->assertSame((bool) $data['boolVal'], $res->getAttribute('boolVal'));
$this->assertSame($data['timestamp'], $res->getAttribute('timestamp'));
$this->assertNull($res->getAttribute('nullableCast'));
}

/**
* Проверяет, что денормалайзер правильно передаст массив в инициированную модель.
*/
public function testDenormalizeWithObjectToPopulate(): void
{
$data = [
'actstatid' => '100',
'@name' => 'test',
'FLOATNUM' => '10.2',
'boolVal' => '1',
'timestamp' => 120000,
'nullableCast' => null,
];
$model = new FiasSerializerMock();

$denormalizer = new EloquentDenormalizer();
$res = $denormalizer->denormalize(
data: $data,
type: FiasSerializerMock::class,
context: [
'object_to_populate' => $model,
]
);

$this->assertInstanceOf(FiasSerializerMock::class, $res);
$this->assertSame($model, $res);
$this->assertSame((int) $data['actstatid'], $res->getAttribute('ACTSTATID'));
$this->assertSame($data['@name'], $res->getAttribute('name'));
$this->assertSame((float) $data['FLOATNUM'], $res->getAttribute('floatNum'));
$this->assertSame((bool) $data['boolVal'], $res->getAttribute('boolVal'));
$this->assertSame($data['timestamp'], $res->getAttribute('timestamp'));
$this->assertNull($res->getAttribute('nullableCast'));
}

/**
* Проверяет, что денормалайзер выьросит исключение, если указана неверная модель для наполнения.
*/
public function testDenormalizeWithObjectToPopulateException(): void
{
$data = [
'actstatid' => '100',
];
$model = new \stdClass();

$denormalizer = new EloquentDenormalizer();

$this->expectException(InvalidArgumentException::class);
$denormalizer->denormalize(
data: $data,
type: FiasSerializerMock::class,
context: [
'object_to_populate' => $model,
]
);
}

/**
* Проверяет, что денормалайзер переъватит исключение из кастера.
*/
public function testDenormalizeNotNormalizableValueException(): void
{
$data = [
'actstatid' => '100',
];

$caster = $this->mock(TypeCaster::class);
$caster->expects($this->once())
->method('canCast')
->willThrowException(new \RuntimeException());

$denormalizer = new EloquentDenormalizer($caster);

$this->expectException(NotNormalizableValueException::class);
$denormalizer->denormalize($data, FiasSerializerMock::class);
}

/**
* Проверяет, что денормалайзер вернет верный список поддерживаемых объектов.
*/
public function testGetSupportedTypes(): void
{
$expected = [
Model::class => true,
];
$caster = $this->mock(TypeCaster::class);

$denormalizer = new EloquentDenormalizer($caster);
$res = $denormalizer->getSupportedTypes(null);

$this->assertSame($expected, $res);
}
}
Loading

0 comments on commit 7027095

Please sign in to comment.