diff --git a/src/Objects/Schema/Generation/AttributeReader.php b/src/Objects/Schema/Generation/AttributeReader.php new file mode 100644 index 0000000..145ba33 --- /dev/null +++ b/src/Objects/Schema/Generation/AttributeReader.php @@ -0,0 +1,33 @@ +getAttributes(); + + return $this->getSchemaAttributes(...$attributes); + } + + public function readPropertyAttributes(\ReflectionProperty $property): SchemaAttributes + { + $attributes = $property->getAttributes(); + + return $this->getSchemaAttributes(...$attributes); + } + + /** + * @param \ReflectionAttribute ...$attributes + */ + private function getSchemaAttributes(\ReflectionAttribute ...$attributes): SchemaAttributes + { + $attributes = array_map(fn ($attr) => $attr->newInstance(), $attributes); + $attributes = array_filter($attributes, fn ($attr) => $attr instanceof SchemaAttribute); + + return new SchemaAttributes(...$attributes); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroAliases.php b/src/Objects/Schema/Generation/Attributes/AvroAliases.php new file mode 100644 index 0000000..b89f10a --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroAliases.php @@ -0,0 +1,39 @@ + + */ + public array $aliases; + + public function __construct( + string ...$aliases, + ) { + $this->aliases = $aliases; + } + + public function name(): string + { + return AttributeName::ALIASES; + } + + public function value(): array + { + return $this->aliases; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroDefault.php b/src/Objects/Schema/Generation/Attributes/AvroDefault.php new file mode 100644 index 0000000..ac6970c --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroDefault.php @@ -0,0 +1,33 @@ +value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroDoc.php b/src/Objects/Schema/Generation/Attributes/AvroDoc.php new file mode 100644 index 0000000..601be62 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroDoc.php @@ -0,0 +1,33 @@ +value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroItems.php b/src/Objects/Schema/Generation/Attributes/AvroItems.php new file mode 100644 index 0000000..188cd94 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroItems.php @@ -0,0 +1,47 @@ +types = array_map(static function ($type) { + if ($type instanceof AvroType) { + return $type; + } + + return new AvroType($type); + }, $types); + } + + /** + * @return AvroType[] + */ + public function value(): array + { + return $this->types; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(...$this->value()); + } + + public function name(): string + { + return AttributeName::ITEMS; + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroName.php b/src/Objects/Schema/Generation/Attributes/AvroName.php new file mode 100644 index 0000000..c9ccc94 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroName.php @@ -0,0 +1,33 @@ +value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroNamespace.php b/src/Objects/Schema/Generation/Attributes/AvroNamespace.php new file mode 100644 index 0000000..37f2000 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroNamespace.php @@ -0,0 +1,33 @@ +value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroOrder.php b/src/Objects/Schema/Generation/Attributes/AvroOrder.php new file mode 100644 index 0000000..7a7c6c5 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroOrder.php @@ -0,0 +1,33 @@ +order->value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroSize.php b/src/Objects/Schema/Generation/Attributes/AvroSize.php new file mode 100644 index 0000000..553919d --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroSize.php @@ -0,0 +1,33 @@ +size; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroSymbols.php b/src/Objects/Schema/Generation/Attributes/AvroSymbols.php new file mode 100644 index 0000000..84367e0 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroSymbols.php @@ -0,0 +1,44 @@ + + */ + private array $symbols; + + /** + * @param non-empty-array $symbols + */ + public function __construct(array $symbols) + { + $this->symbols = $symbols; + } + + public function name(): string + { + return AttributeName::SYMBOLS; + } + + /** + * @return array + */ + public function value(): array + { + return $this->symbols; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroTargetClass.php b/src/Objects/Schema/Generation/Attributes/AvroTargetClass.php new file mode 100644 index 0000000..b5d9c1f --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroTargetClass.php @@ -0,0 +1,33 @@ +value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroType.php b/src/Objects/Schema/Generation/Attributes/AvroType.php new file mode 100644 index 0000000..f7d084b --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroType.php @@ -0,0 +1,44 @@ + + */ + public array $attributes = []; + + public string $value; + + public function __construct( + Type|string $value, + SchemaAttribute ...$attributes, + ) { + $this->value = \is_string($value) ? $value : $value->value; + + $this->attributes = $attributes; + } + + public function name(): string + { + return AttributeName::TYPE; + } + + public function value(): string + { + return $this->value; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(...$this->attributes); + } +} diff --git a/src/Objects/Schema/Generation/Attributes/AvroValues.php b/src/Objects/Schema/Generation/Attributes/AvroValues.php new file mode 100644 index 0000000..b69f319 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/AvroValues.php @@ -0,0 +1,47 @@ +types = array_map(function ($type) { + if ($type instanceof AvroType) { + return $type; + } + + return new AvroType($type); + }, $types); + } + + /** + * @return AvroType[] + */ + public function value(): array + { + return $this->types; + } + + public function attributes(): SchemaAttributes + { + return new SchemaAttributes(...$this->value()); + } + + public function name(): string + { + return AttributeName::VALUES; + } +} diff --git a/src/Objects/Schema/Generation/Attributes/Order.php b/src/Objects/Schema/Generation/Attributes/Order.php new file mode 100644 index 0000000..32ad437 --- /dev/null +++ b/src/Objects/Schema/Generation/Attributes/Order.php @@ -0,0 +1,12 @@ + $this->simpleType(Schema::map()), TypeName::ENUM => $this->simpleType(Schema::enum()), TypeName::FIXED => $this->simpleType(Schema::fixed()), + TypeName::UUID => $this->simpleType(Schema::uuid()), + TypeName::TIME_MICROS => $this->simpleType(Schema::timeMicros()), + TypeName::TIME_MILLIS => $this->simpleType(Schema::timeMillis()), + TypeName::TIMESTAMP_MICROS => $this->simpleType(Schema::timestampMicros()), + TypeName::TIMESTAMP_MILLIS => $this->simpleType(Schema::timestampMillis()), + TypeName::LOCAL_TIMESTAMP_MICROS => $this->simpleType(Schema::localTimestampMicros()), + TypeName::LOCAL_TIMESTAMP_MILLIS => $this->simpleType(Schema::localTimestampMillis()), ]; } diff --git a/test/Objects/Schema/Generation/Fixture/ArraysWithComplexType.php b/test/Objects/Schema/Generation/Fixture/ArraysWithComplexType.php index e6cbad8..aed36ec 100644 --- a/test/Objects/Schema/Generation/Fixture/ArraysWithComplexType.php +++ b/test/Objects/Schema/Generation/Fixture/ArraysWithComplexType.php @@ -5,12 +5,19 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroItems; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroValues; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\Type; /** * @SerDe\AvroType("record") * * @SerDe\AvroName("ArraysWithComplexType") */ +#[AvroType('record')] +#[AvroName('ArraysWithComplexType')] class ArraysWithComplexType { /** @@ -23,6 +30,13 @@ class ArraysWithComplexType * }) * }) */ + #[AvroType( + 'array', + attributes: new AvroItems( + new AvroType(Type::STRING), + new AvroType(Type::ARRAY, new AvroItems(new AvroType(Type::STRING))), + ) + )] private array $arrayWithUnion; /** @@ -34,5 +48,14 @@ class ArraysWithComplexType * ) * }) */ + #[AvroType( + Type::ARRAY, + attributes: new AvroItems( + new AvroType( + Type::MAP, + new AvroValues(new AvroType(Type::STRING)) + ), + ) + )] private array $arrayWithMap; } diff --git a/test/Objects/Schema/Generation/Fixture/EmptyRecord.php b/test/Objects/Schema/Generation/Fixture/EmptyRecord.php index e205cdc..6ca4dc4 100644 --- a/test/Objects/Schema/Generation/Fixture/EmptyRecord.php +++ b/test/Objects/Schema/Generation/Fixture/EmptyRecord.php @@ -5,6 +5,9 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroNamespace; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; /** * @SerDe\AvroName("EmptyRecord") @@ -13,6 +16,9 @@ * * @SerDe\AvroType("record") */ +#[AvroName('EmptyRecord')] +#[AvroNamespace('org.acme')] +#[AvroType('record')] class EmptyRecord { } diff --git a/test/Objects/Schema/Generation/Fixture/MapsWithComplexType.php b/test/Objects/Schema/Generation/Fixture/MapsWithComplexType.php index 46d4368..1438c84 100644 --- a/test/Objects/Schema/Generation/Fixture/MapsWithComplexType.php +++ b/test/Objects/Schema/Generation/Fixture/MapsWithComplexType.php @@ -5,12 +5,19 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroItems; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroValues; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\Type; /** * @SerDe\AvroType("record") * * @SerDe\AvroName("MapsWithComplexType") */ +#[AvroType(Type::RECORD)] +#[AvroName('MapsWithComplexType')] class MapsWithComplexType { /** @@ -23,6 +30,16 @@ class MapsWithComplexType * }) * }) */ + #[AvroType( + Type::MAP, + new AvroValues( + Type::STRING, + new AvroType( + Type::ARRAY, + new AvroItems(Type::STRING) + ) + ) + )] private $mapWithUnion; /** @@ -34,5 +51,14 @@ class MapsWithComplexType * ) * }) */ + #[AvroType( + Type::MAP, + new AvroValues( + new AvroType( + Type::ARRAY, + new AvroItems(Type::STRING) + ) + ) + )] private $mapWithArray; } diff --git a/test/Objects/Schema/Generation/Fixture/PrimitiveTypes.php b/test/Objects/Schema/Generation/Fixture/PrimitiveTypes.php index cefa07d..1dffba9 100644 --- a/test/Objects/Schema/Generation/Fixture/PrimitiveTypes.php +++ b/test/Objects/Schema/Generation/Fixture/PrimitiveTypes.php @@ -5,6 +5,14 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroAliases; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroDefault; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroDoc; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroNamespace; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroOrder; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\Order; /** * @SerDe\AvroName("PrimitiveTypes") @@ -13,6 +21,9 @@ * * @SerDe\AvroType("record") */ +#[AvroName('PrimitiveTypes')] +#[AvroNamespace('org.acme')] +#[AvroType('record')] class PrimitiveTypes { /** @@ -20,6 +31,8 @@ class PrimitiveTypes * * @SerDe\AvroType("null") */ + #[AvroDoc('null type')] + #[AvroType('null')] private $nullType; /** @@ -29,11 +42,15 @@ class PrimitiveTypes * * @SerDe\AvroType("boolean") */ + #[AvroName('isItTrue')] + #[AvroDefault(false)] + #[AvroType('boolean')] private $booleanType; /** * @SerDe\AvroType("int") */ + #[AvroType('int')] private $intType; /** @@ -41,6 +58,8 @@ class PrimitiveTypes * * @SerDe\AvroOrder("ascending") */ + #[AvroType('long')] + #[AvroOrder(Order::ASC)] private $longType; /** @@ -48,20 +67,25 @@ class PrimitiveTypes * * @SerDe\AvroAliases({"foo", "bar"}) */ + #[AvroType('float')] + #[AvroAliases('foo', 'bar')] private $floatType; /** * @SerDe\AvroType("double") */ + #[AvroType('double')] private $doubleType; /** * @SerDe\AvroType("bytes") */ + #[AvroType('bytes')] private $bytesType; /** * @SerDe\AvroType("string") */ + #[AvroType('string')] private $stringType; } diff --git a/test/Objects/Schema/Generation/Fixture/RecordWithComplexTypes.php b/test/Objects/Schema/Generation/Fixture/RecordWithComplexTypes.php index de26e76..5280126 100644 --- a/test/Objects/Schema/Generation/Fixture/RecordWithComplexTypes.php +++ b/test/Objects/Schema/Generation/Fixture/RecordWithComplexTypes.php @@ -5,12 +5,26 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroAliases; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroDefault; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroItems; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroNamespace; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroOrder; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroSize; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroSymbols; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroValues; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\Order; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\Type; /** * @SerDe\AvroType("record") * * @SerDe\AvroName("RecordWithComplexTypes") */ +#[AvroType('record')] +#[AvroName('RecordWithComplexTypes')] class RecordWithComplexTypes { /** @@ -21,6 +35,10 @@ class RecordWithComplexTypes * @SerDe\AvroDefault({"foo", "bar"}), * }) */ + #[AvroType( + 'array', + new AvroItems(Type::STRING), new AvroDefault(['foo', 'bar']) + )] private $array; /** @@ -31,6 +49,10 @@ class RecordWithComplexTypes * @SerDe\AvroDefault({"foo": 42, "bar": 42}), * }) */ + #[AvroType( + 'map', + new AvroValues(Type::INT), new AvroDefault(['foo' => 42, 'bar' => 42]) + )] private $map; /** @@ -43,6 +65,12 @@ class RecordWithComplexTypes * @SerDe\AvroSymbols({"SPADES", "HEARTS", "DIAMONDS", "CLUBS"}) * }) */ + #[AvroOrder(Order::ASC)] + #[AvroType( + Type::ENUM, + new AvroName('Suit'), + new AvroSymbols(['SPADES', 'HEARTS', 'DIAMONDS', 'CLUBS']) + )] private $enum; /** @@ -57,6 +85,13 @@ class RecordWithComplexTypes * @SerDe\AvroSize(16) * }) */ + #[AvroType( + Type::FIXED, + new AvroName('md5'), + new AvroNamespace('org.acme'), + new AvroAliases('foo', 'bar'), + new AvroSize(16) + )] private $fixed; /** @@ -67,5 +102,11 @@ class RecordWithComplexTypes * @SerDe\AvroItems("string"), * }) */ + #[AvroType(Type::STRING)] + #[AvroType(Type::INT)] + #[AvroType( + Type::ARRAY, + new AvroItems(Type::STRING) + )] private $union; } diff --git a/test/Objects/Schema/Generation/Fixture/RecordWithRecordType.php b/test/Objects/Schema/Generation/Fixture/RecordWithRecordType.php index 7f9a85e..2a90288 100644 --- a/test/Objects/Schema/Generation/Fixture/RecordWithRecordType.php +++ b/test/Objects/Schema/Generation/Fixture/RecordWithRecordType.php @@ -5,12 +5,18 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroDoc; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroTargetClass; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; /** * @SerDe\AvroType("record") * * @SerDe\AvroName("RecordWithRecordType") */ +#[AvroType('record')] +#[AvroName('RecordWithRecordType')] class RecordWithRecordType { /** @@ -23,7 +29,12 @@ class RecordWithRecordType * @SerDe\AvroDoc("This a simple record for testing purposes") * }) */ - private $simpleRecord; + #[AvroName('simpleField')] + #[AvroType('record', + new AvroTargetClass(SimpleRecord::class), + new AvroDoc('This a simple record for testing purposes') + )] + private SimpleRecord $simpleRecord; /** * @SerDe\AvroName("unionField") @@ -31,5 +42,8 @@ class RecordWithRecordType * @SerDe\AvroType("null") * @SerDe\AvroType("org.acme.SimpleRecord") */ - private $unionRecord; + #[AvroName('unionField')] + #[AvroType('null')] + #[AvroType('org.acme.SimpleRecord')] + private ?SimpleRecord $unionRecord; } diff --git a/test/Objects/Schema/Generation/Fixture/SimpleRecord.php b/test/Objects/Schema/Generation/Fixture/SimpleRecord.php index 3d0a7b5..148d402 100644 --- a/test/Objects/Schema/Generation/Fixture/SimpleRecord.php +++ b/test/Objects/Schema/Generation/Fixture/SimpleRecord.php @@ -5,6 +5,10 @@ namespace FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture; use FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations as SerDe; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroDefault; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroName; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroNamespace; +use FlixTech\AvroSerializer\Objects\Schema\Generation\Attributes\AvroType; /** * @SerDe\AvroName("SimpleRecord") @@ -13,6 +17,9 @@ * * @SerDe\AvroType("record") */ +#[AvroName('SimpleRecord')] +#[AvroNamespace('org.acme')] +#[AvroType('record')] class SimpleRecord { /** @@ -20,5 +27,19 @@ class SimpleRecord * * @SerDe\AvroDefault(42) */ + #[AvroType('int')] + #[AvroDefault(42)] private $intType; + + /** + * @SerDe\AvroType("uuid") + */ + #[AvroType('uuid')] + private $uuidType; + + /** + * @SerDe\AvroType("timestamp-millis") + */ + #[AvroType('timestamp-millis')] + private $timestampMillisType; } diff --git a/test/Objects/Schema/Generation/SchemaGeneratorTest.php b/test/Objects/Schema/Generation/SchemaGeneratorTest.php index 757b3dd..a9ef7a5 100644 --- a/test/Objects/Schema/Generation/SchemaGeneratorTest.php +++ b/test/Objects/Schema/Generation/SchemaGeneratorTest.php @@ -6,6 +6,7 @@ use Doctrine\Common\Annotations\AnnotationReader; use FlixTech\AvroSerializer\Objects\Schema; +use FlixTech\AvroSerializer\Objects\Schema\Generation\AttributeReader; use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaGenerator; use FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\ArraysWithComplexType; use FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\EmptyRecord; @@ -13,225 +14,216 @@ use FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\PrimitiveTypes; use FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\RecordWithComplexTypes; use FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\RecordWithRecordType; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; class SchemaGeneratorTest extends TestCase { - private SchemaGenerator $generator; + private SchemaGenerator $generatorDoctrineAnnotations; + + private SchemaGenerator $generatorAttributes; protected function setUp(): void { - $this->generator = new SchemaGenerator( + $this->generatorDoctrineAnnotations = new SchemaGenerator( new Schema\Generation\AnnotationReader( new AnnotationReader() ) ); - } - /** - * @throws \ReflectionException - */ - #[Test] - public function it_should_generate_an_empty_record(): void - { - $schema = $this->generator->generate(EmptyRecord::class); - - $expected = Schema::record() - ->name('EmptyRecord') - ->namespace('org.acme'); - - $this->assertEquals($expected, $schema); + $this->generatorAttributes = new SchemaGenerator( + new AttributeReader() + ); } /** * @throws \ReflectionException */ #[Test] - public function it_should_generate_a_record_schema_with_primitive_types(): void + #[DataProvider('schemaDataProvider')] + public function it_should_generate_schema_using_doctrine_annotations(string $class, Schema $expectedSchema): void { - $schema = $this->generator->generate(PrimitiveTypes::class); - - $expected = Schema::record() - ->name('PrimitiveTypes') - ->namespace('org.acme') - ->field( - 'nullType', - Schema::null(), - Schema\Record\FieldOption::doc('null type') - ) - ->field( - 'isItTrue', - Schema::boolean(), - Schema\Record\FieldOption::default(false) - ) - ->field( - 'intType', - Schema::int() - ) - ->field( - 'longType', - Schema::long(), - Schema\Record\FieldOption::orderAsc() - ) - ->field( - 'floatType', - Schema::float(), - Schema\Record\FieldOption::aliases('foo', 'bar') - ) - ->field( - 'doubleType', - Schema::double() - ) - ->field( - 'bytesType', - Schema::bytes() - ) - ->field( - 'stringType', - Schema::string() - ); + $actualSchema = $this->generatorDoctrineAnnotations->generate($class); - $this->assertEquals($expected, $schema); + self::assertEquals($expectedSchema, $actualSchema); } /** * @throws \ReflectionException */ #[Test] - public function it_should_generate_a_schema_record_with_complex_types(): void + #[DataProvider('schemaDataProvider')] + public function it_should_generate_schema_using_attributes(string $class, Schema $expectedSchema): void { - $schema = $this->generator->generate(RecordWithComplexTypes::class); + $actualSchema = $this->generatorAttributes->generate($class); - $expected = Schema::record() - ->name('RecordWithComplexTypes') - ->field( - 'array', - Schema::array() - ->items(Schema::string()) - ->default(['foo', 'bar']) - ) - ->field( - 'map', - Schema::map() - ->values(Schema::int()) - ->default(['foo' => 42, 'bar' => 42]) - ) - ->field( - 'enum', - Schema::enum() - ->name('Suit') - ->symbols('SPADES', 'HEARTS', 'DIAMONDS', 'CLUBS'), - Schema\Record\FieldOrder::asc() - ) - ->field( - 'fixed', - Schema::fixed() - ->name('md5') - ->namespace('org.acme') - ->aliases('foo', 'bar') - ->size(16) - ) - ->field( - 'union', - Schema::union(Schema::string(), Schema::int(), Schema::array()->items(Schema::string())) - ); - - $this->assertEquals($expected, $schema); + self::assertEquals($expectedSchema, $actualSchema); } - /** - * @throws \ReflectionException - */ - #[Test] - public function it_should_generate_records_containing_records(): void + public static function schemaDataProvider(): array { - $schema = $this->generator->generate(RecordWithRecordType::class); - - $expected = Schema::record() - ->name('RecordWithRecordType') - ->field( - 'simpleField', - Schema::record() - ->name('SimpleRecord') + return [ + 'empty record' => [ + 'class' => EmptyRecord::class, + 'expectedSchema' => Schema::record() + ->name('EmptyRecord') + ->namespace('org.acme'), + ], + 'primitive types' => [ + 'class' => PrimitiveTypes::class, + 'expectedSchema' => Schema::record() + ->name('PrimitiveTypes') ->namespace('org.acme') - ->doc('This a simple record for testing purposes') + ->field( + 'nullType', + Schema::null(), + Schema\Record\FieldOption::doc('null type') + ) + ->field( + 'isItTrue', + Schema::boolean(), + Schema\Record\FieldOption::default(false) + ) ->field( 'intType', - Schema::int(), - Schema\Record\FieldOption::default(42) + Schema::int() + ) + ->field( + 'longType', + Schema::long(), + Schema\Record\FieldOption::orderAsc() + ) + ->field( + 'floatType', + Schema::float(), + Schema\Record\FieldOption::aliases('foo', 'bar') + ) + ->field( + 'doubleType', + Schema::double() + ) + ->field( + 'bytesType', + Schema::bytes() + ) + ->field( + 'stringType', + Schema::string() ), - ) - ->field( - 'unionField', - Schema::union( - Schema::null(), - Schema::named('org.acme.SimpleRecord') - ) - ); - - $this->assertEquals($expected, $schema); - } - - /** - * @throws \ReflectionException - */ - #[Test] - public function it_should_generate_a_record_schema_with_arrays_containing_complex_types(): void - { - $schema = $this->generator->generate(ArraysWithComplexType::class); - - $expected = Schema::record() - ->name('ArraysWithComplexType') - ->field( - 'arrayWithUnion', - Schema::array() - ->items( - Schema::union( - Schema::string(), - Schema::array()->items(Schema::string()) - ) + ], + 'record with complex types' => [ + 'class' => RecordWithComplexTypes::class, + 'expectedSchema' => Schema::record() + ->name('RecordWithComplexTypes') + ->field( + 'array', + Schema::array() + ->items(Schema::string()) + ->default(['foo', 'bar']) ) - ) - ->field( - 'arrayWithMap', - Schema::array() - ->items( - Schema::map()->values(Schema::string()) + ->field( + 'map', + Schema::map() + ->values(Schema::int()) + ->default(['foo' => 42, 'bar' => 42]) ) - ); - - $this->assertEquals($expected, $schema); - } - - /** - * @throws \ReflectionException - */ - #[Test] - public function it_should_generate_a_record_schema_with_maps_containing_complex_types(): void - { - $schema = $this->generator->generate(MapsWithComplexType::class); - - $expected = Schema::record() - ->name('MapsWithComplexType') - ->field( - 'mapWithUnion', - Schema::map() - ->values( + ->field( + 'enum', + Schema::enum() + ->name('Suit') + ->symbols('SPADES', 'HEARTS', 'DIAMONDS', 'CLUBS'), + Schema\Record\FieldOrder::asc() + ) + ->field( + 'fixed', + Schema::fixed() + ->name('md5') + ->namespace('org.acme') + ->aliases('foo', 'bar') + ->size(16) + ) + ->field( + 'union', + Schema::union(Schema::string(), Schema::int(), Schema::array()->items(Schema::string())) + ), + ], + 'record with record type' => [ + 'class' => RecordWithRecordType::class, + 'expectedSchema' => Schema::record() + ->name('RecordWithRecordType') + ->field( + 'simpleField', + Schema::record() + ->name('SimpleRecord') + ->namespace('org.acme') + ->doc('This a simple record for testing purposes') + ->field( + 'intType', + Schema::int(), + Schema\Record\FieldOption::default(42) + ) + ->field( + 'uuidType', + Schema::uuid() + ) + ->field( + 'timestampMillisType', + Schema::timestampMillis() + ), + ) + ->field( + 'unionField', Schema::union( - Schema::string(), - Schema::array()->items(Schema::string()) + Schema::null(), + Schema::named('org.acme.SimpleRecord') ) + ), + ], + 'arrays with complex type' => [ + 'class' => ArraysWithComplexType::class, + 'expectedSchema' => Schema::record() + ->name('ArraysWithComplexType') + ->field( + 'arrayWithUnion', + Schema::array() + ->items( + Schema::union( + Schema::string(), + Schema::array()->items(Schema::string()) + ) + ) ) - ) - ->field( - 'mapWithArray', - Schema::map() - ->values( - Schema::array()->items(Schema::string()) + ->field( + 'arrayWithMap', + Schema::array() + ->items( + Schema::map()->values(Schema::string()) + ) + ), + ], + 'maps with complex type' => [ + 'class' => MapsWithComplexType::class, + 'expectedSchema' => Schema::record() + ->name('MapsWithComplexType') + ->field( + 'mapWithUnion', + Schema::map() + ->values( + Schema::union( + Schema::string(), + Schema::array()->items(Schema::string()) + ) + ) ) - ); - - $this->assertEquals($expected, $schema); + ->field( + 'mapWithArray', + Schema::map() + ->values( + Schema::array()->items(Schema::string()) + ) + ), + ], + ]; } }