Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function test_run_schema() : void

self::assertCommandOutputIdentical(
<<<'OUTPUT'
[{"ref":"order_id","type":{"type":"uuid"},"nullable":false,"metadata":[]},{"ref":"created_at","type":{"type":"string"},"nullable":false,"metadata":[]},{"ref":"updated_at","type":{"type":"string"},"nullable":false,"metadata":[]},{"ref":"discount","type":{"type":"string"},"nullable":true,"metadata":[]},{"ref":"address","type":{"type":"json"},"nullable":false,"metadata":[]},{"ref":"notes","type":{"type":"json"},"nullable":false,"metadata":[]},{"ref":"items","type":{"type":"json"},"nullable":false,"metadata":[]}]
[{"ref":"order_id","type":{"type":"string"},"nullable":false,"metadata":[]},{"ref":"created_at","type":{"type":"string"},"nullable":false,"metadata":[]},{"ref":"updated_at","type":{"type":"string"},"nullable":false,"metadata":[]},{"ref":"discount","type":{"type":"string"},"nullable":true,"metadata":[]},{"ref":"address","type":{"type":"json"},"nullable":false,"metadata":[]},{"ref":"notes","type":{"type":"json"},"nullable":false,"metadata":[]},{"ref":"items","type":{"type":"json"},"nullable":false,"metadata":[]}]

OUTPUT,
$tester->getDisplay()
Expand All @@ -40,7 +40,7 @@ public function test_run_schema_as_ascii() : void
self::assertCommandOutputIdentical(
<<<'OUTPUT'
schema
|-- order_id: uuid
|-- order_id: string
|-- created_at: string
|-- updated_at: string
|-- discount: ?string
Expand Down Expand Up @@ -85,7 +85,7 @@ public function test_run_schema_with_offset() : void
self::assertCommandOutputIdentical(
<<<'OUTPUT'
schema
|-- order_id: uuid
|-- order_id: string
|-- created_at: string
|-- updated_at: string
|-- discount: ?string
Expand Down Expand Up @@ -117,7 +117,7 @@ public function test_run_schema_with_offset_and_limit() : void
+------------+--------+----------+----------+
| name | type | nullable | metadata |
+------------+--------+----------+----------+
| order_id | uuid | false | [] |
| order_id | string | false | [] |
| created_at | string | false | [] |
| updated_at | string | false | [] |
| discount | string | false | [] |
Expand All @@ -143,7 +143,7 @@ public function test_run_schema_with_php_output() : void
self::assertCommandOutputIdentical(
<<<'OUTPUT'
\Flow\ETL\DSL\schema(
\Flow\ETL\DSL\uuid_schema("order_id", nullable: false, metadata: \Flow\ETL\DSL\schema_metadata()),
\Flow\ETL\DSL\string_schema("order_id", nullable: false, metadata: \Flow\ETL\DSL\schema_metadata()),
\Flow\ETL\DSL\string_schema("created_at", nullable: false, metadata: \Flow\ETL\DSL\schema_metadata()),
\Flow\ETL\DSL\string_schema("updated_at", nullable: false, metadata: \Flow\ETL\DSL\schema_metadata()),
\Flow\ETL\DSL\string_schema("discount", nullable: true, metadata: \Flow\ETL\DSL\schema_metadata()),
Expand Down Expand Up @@ -171,7 +171,7 @@ public function test_run_schema_with_pretty_output() : void
{
"ref": "order_id",
"type": {
"type": "uuid"
"type": "string"
},
"nullable": false,
"metadata": []
Expand Down Expand Up @@ -244,7 +244,7 @@ public function test_run_schema_with_table_output() : void
+------------+--------+----------+----------+
| name | type | nullable | metadata |
+------------+--------+----------+----------+
| order_id | uuid | false | [] |
| order_id | string | false | [] |
| created_at | string | false | [] |
| updated_at | string | false | [] |
| discount | string | true | [] |
Expand Down Expand Up @@ -328,7 +328,7 @@ public function test_run_schema_with_table_output_on_excel() : void
+------------+--------+----------+----------+
| name | type | nullable | metadata |
+------------+--------+----------+----------+
| order_id | uuid | false | [] |
| order_id | string | false | [] |
| created_at | string | false | [] |
| updated_at | string | false | [] |
| discount | string | true | [] |
Expand Down Expand Up @@ -463,7 +463,7 @@ public function test_run_schema_with_zero_offset() : void
self::assertCommandOutputIdentical(
<<<'OUTPUT'
schema
|-- order_id: uuid
|-- order_id: string
|-- created_at: string
|-- updated_at: string
|-- discount: ?string
Expand Down
63 changes: 7 additions & 56 deletions src/core/etl/src/Flow/ETL/Row/EntryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,16 @@ enum_entry,
uuid_entry,
xml_element_entry,
xml_entry};
use function Flow\Types\DSL\{type_date,
type_datetime,
type_html,
type_json,
type_optional,
type_string,
type_time,
type_uuid,
type_xml,
type_xml_element};
use Flow\ETL\Exception\{InvalidArgumentException,
RuntimeException,
SchemaDefinitionNotFoundException};
use function Flow\Types\DSL\{type_optional, type_string};
use Flow\ETL\Exception\{InvalidArgumentException, SchemaDefinitionNotFoundException};
use Flow\ETL\Row\Entry\{ListEntry, MapEntry, StringEntry, StructureEntry};
use Flow\ETL\Schema;
use Flow\ETL\Schema\{Definition, Metadata};
use Flow\Types\Exception\CastingException;
use Flow\Types\Type;
use Flow\Types\Type\Logical\{DateTimeType,
DateType,
HTMLType,
InstanceOfType,
JsonType,
ListType,
Expand All @@ -52,7 +42,6 @@ enum_entry,
UuidType,
XMLElementType,
XMLType};
use Flow\Types\Type\Logical\HTMLType;
use Flow\Types\Type\Native\{
ArrayType,
BooleanType,
Expand All @@ -63,17 +52,14 @@ enum_entry,
StringType,
UnionType
};
use Flow\Types\Type\Native\String\StringTypeChecker;
use Flow\Types\Type\TypeDetector;
use Flow\Types\Value\Uuid;

final readonly class EntryFactory
{
/**
* @param null|Definition<mixed>|Schema $schema
*
* @throws InvalidArgumentException
* @throws RuntimeException
* @throws SchemaDefinitionNotFoundException
*
* @return Entry<mixed>
Expand All @@ -96,45 +82,6 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $

$valueType = (new TypeDetector())->detectType($value);

if ($valueType instanceof StringType) {
$value = type_string()->assert($value);
$stringChecker = new StringTypeChecker($value);

if ($stringChecker->isJson()) {
$valueType = type_json();
} elseif ($stringChecker->isUuid()) {
$valueType = type_uuid();
} elseif ($stringChecker->isHTML()) {
$valueType = type_html();
} elseif ($stringChecker->isXML()) {
$valueType = type_xml();
}
}

if ($valueType instanceof InstanceOfType) {
if ($valueType->class === \DOMDocument::class) {
$valueType = type_xml();
} elseif ($valueType->class === \DOMElement::class) {
$valueType = type_xml_element();
} elseif ($valueType->class === \DateInterval::class) {
$valueType = type_time();
} elseif (\in_array($valueType->class, [\DateTimeImmutable::class, \DateTimeInterface::class, \DateTime::class], true)) {
if ($value instanceof \DateTimeInterface && $value->format('H:i:s') === '00:00:00') {
$valueType = type_date();
} else {
$valueType = type_datetime();
}
} else {
foreach (['Ramsey\Uuid\UuidInterface', Uuid::class, 'Symfony\Component\Uid\Uuid'] as $uuidClass) {
if (\is_a($valueType->class, $uuidClass, true)) {
$valueType = type_uuid();

break;
}
}
}
}

return $this->createAs($entryName, $value, $valueType);
}

Expand Down Expand Up @@ -223,6 +170,10 @@ public function createAs(string $entryName, mixed $value, Definition|Type $defin
return string_entry($entryName, type_optional(type_string())->cast($value), $metadata);
}

if ($type instanceof NullType) {
return StringEntry::fromNull($entryName, $metadata);
}

if ($type instanceof EnumType) {
$castValue = type_optional($type)->cast($value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static function cast_provider() : array
'xml_to_array' => [$xml, 'array', ['root' => ['foo' => ['@attributes' => ['baz' => 'buz'], '@value' => 'bar']]]],
'string_to_xml' => [$xmlString, 'xml', $xml],
'xml_to_string' => [$xml, 'string', '<root><foo baz="buz">bar</foo></root>'],
'full_xml_to_string' => [$fullXMLString, 'string', '<root><foo baz="buz">bar</foo></root>'],
'full_xml_to_string' => [$fullXMLString, 'string', $fullXMLString],
'datetime' => [new \DateTimeImmutable('2023-01-01 00:00:00 UTC'), 'string', '2023-01-01T00:00:00+00:00'],
'datetime_to_date' => [new \DateTimeImmutable('2023-01-01 00:01:00 UTC'), 'date', new \DateTimeImmutable('2023-01-01T00:00:00+00:00')],
'string_to_timezone' => ['UTC', 'timezone', new \DateTimeZone('UTC')],
Expand Down
32 changes: 21 additions & 11 deletions src/core/etl/tests/Flow/ETL/Tests/Unit/Row/EntryFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum_entry,
json_object_entry,
list_entry,
str_entry,
string_entry,
time_entry,
uuid_entry,
xml_entry};
Expand All @@ -35,9 +36,9 @@ enum_schema,
time_schema,
uuid_schema,
xml_schema};
use function Flow\Types\DSL\{type_datetime, type_float, type_integer, type_list, type_map, type_string, type_structure, type_time_zone};
use function Flow\Types\DSL\{type_datetime, type_float, type_integer, type_list, type_map, type_null, type_string, type_structure, type_time_zone};
use Flow\ETL\Exception\{InvalidArgumentException, SchemaDefinitionNotFoundException};
use Flow\ETL\Row\Entry\TimeEntry;
use Flow\ETL\Row\Entry\{StringEntry, TimeEntry};
use Flow\ETL\Row\{Entry, EntryFactory};
use Flow\ETL\Schema\Metadata;
use Flow\ETL\Tests\Fixtures\Enum\BackedIntEnum;
Expand All @@ -59,17 +60,17 @@ public static function provide_recognized_data() : \Generator

yield 'xml' => [
$xml = '<root><foo>1</foo><bar>2</bar><baz>3</baz></root>',
xml_entry('e', $xml),
string_entry('e', $xml),
];

yield 'html' => [
$html = '<!DOCTYPE html><html><head></head><body><div id="id">2</div><p>3</p></body></html>',
html_entry('e', $html),
string_entry('e', $html),
];

yield 'uuid' => [
$uuid = '00000000-0000-0000-0000-000000000000',
uuid_entry('e', $uuid),
string_entry('e', $uuid),
];
}

Expand Down Expand Up @@ -263,7 +264,7 @@ public function test_html_from_dom_document() : void
public function test_html_from_string() : void
{
self::assertEquals(
html_entry('e', $html = '<!DOCTYPE html><html><head></head><body><div>foo</div><p>3</p></body></html>'),
string_entry('e', $html = '<!DOCTYPE html><html><head></head><body><div>foo</div><p>3</p></body></html>'),
$this->entryFactory->create('e', $html)
);
}
Expand Down Expand Up @@ -418,6 +419,14 @@ public function test_nested_structure() : void
);
}

public function test_null_type_handled() : void
{
self::assertEquals(
StringEntry::fromNull('e'),
$this->entryFactory->createAs('e', null, type_null())
);
}

public function test_object() : void
{
$this->expectExceptionMessage("e: object<ArrayIterator> can't be converted to any known Entry, please normalize that object first");
Expand Down Expand Up @@ -521,16 +530,17 @@ public function test_uuid_from_ramsey_uuid_library() : void
self::markTestSkipped("Package 'ramsey/uuid' is required for this test.");
}

$uuidObject = Uuid::uuid4();
self::assertEquals(
uuid_entry('e', $uuid = Uuid::uuid4()->toString()),
$this->entryFactory->create('e', $uuid)
uuid_entry('e', $uuidObject->toString()),
$this->entryFactory->create('e', $uuidObject)
);
}

public function test_uuid_from_string() : void
{
self::assertEquals(
uuid_entry('e', $uuid = '00000000-0000-0000-0000-000000000000'),
string_entry('e', $uuid = '00000000-0000-0000-0000-000000000000'),
$this->entryFactory->create('e', $uuid)
);
}
Expand All @@ -546,7 +556,7 @@ public function test_uuid_string_with_uuid_definition_provided() : void
public function test_uuid_type() : void
{
self::assertEquals(
uuid_entry('e', '00000000-0000-0000-0000-000000000000'),
string_entry('e', '00000000-0000-0000-0000-000000000000'),
$this->entryFactory->create('e', '00000000-0000-0000-0000-000000000000')
);
}
Expand Down Expand Up @@ -580,7 +590,7 @@ public function test_xml_from_dom_document() : void
public function test_xml_from_string() : void
{
self::assertEquals(
xml_entry('e', $xml = '<root><foo>1</foo><bar>2</bar><baz>3</baz></root>'),
string_entry('e', $xml = '<root><foo>1</foo><bar>2</bar><baz>3</baz></root>'),
$this->entryFactory->create('e', $xml)
);
}
Expand Down
51 changes: 6 additions & 45 deletions src/lib/types/src/Flow/Types/Type/AutoCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@

namespace Flow\Types\Type;

use function Flow\Types\DSL\{get_type,
type_boolean,
type_date,
type_datetime,
type_float,
type_integer,
type_json,
type_time_zone,
type_uuid};
use Flow\Types\Type\Native\String\StringTypeChecker;
use function Flow\Types\DSL\{get_type, type_float};
use Flow\Types\Type\Native\NullType;
use Flow\Types\Type\Native\String\StringTypeNarrower;

final readonly class AutoCaster
{
Expand Down Expand Up @@ -62,44 +55,12 @@ private function castArray(array $value) : array

private function castToString(string $value) : mixed
{
$typeChecker = new StringTypeChecker($value);
$narrowedType = (new StringTypeNarrower())->narrow($value);

if ($typeChecker->isNull()) {
if ($narrowedType instanceof NullType) {
return null;
}

if ($typeChecker->isInteger()) {
return type_integer()->cast($value);
}

if ($typeChecker->isFloat()) {
return type_float()->cast($value);
}

if ($typeChecker->isBoolean()) {
return type_boolean()->cast($value);
}

if ($typeChecker->isJson()) {
return type_json()->cast($value);
}

if ($typeChecker->isUuid()) {
return type_uuid()->cast($value);
}

if ($typeChecker->isTimeZone()) {
return type_time_zone()->cast($value);
}

if ($typeChecker->isDate()) {
return type_date()->cast($value);
}

if ($typeChecker->isDateTime()) {
return type_datetime()->cast($value);
}

return $value;
return $narrowedType->cast($value);
}
}
Loading
Loading