diff --git a/docs/components/core/building-blocks.md b/docs/components/core/building-blocks.md index 82a3b45b8..9170d133c 100644 --- a/docs/components/core/building-blocks.md +++ b/docs/components/core/building-blocks.md @@ -45,8 +45,9 @@ $rows = array_to_rows([ ## Entry Types -- [Array](../../../src/core/etl/src/Flow/ETL/Row/Entry/ArrayEntry.php) - [Boolean](../../../src/core/etl/src/Flow/ETL/Row/Entry/BooleanEntry.php) +- [Time](../../../src/core/etl/src/Flow/ETL/Row/Entry/TimeEntry.php) +- [Date](../../../src/core/etl/src/Flow/ETL/Row/Entry/DateEntry.php) - [DateTime](../../../src/core/etl/src/Flow/ETL/Row/Entry/DateTimeEntry.php) - [Enum](../../../src/core/etl/src/Flow/ETL/Row/Entry/EnumEntry.php) - [Float](../../../src/core/etl/src/Flow/ETL/Row/Entry/FloatEntry.php) @@ -54,7 +55,6 @@ $rows = array_to_rows([ - [Json](../../../src/core/etl/src/Flow/ETL/Row/Entry/JsonEntry.php) - [List](../../../src/core/etl/src/Flow/ETL/Row/Entry/ListEntry.php) - [Map](../../../src/core/etl/src/Flow/ETL/Row/Entry/MapEntry.php) -- [Object](../../../src/core/etl/src/Flow/ETL/Row/Entry/ObjectEntry.php) - [String](../../../src/core/etl/src/Flow/ETL/Row/Entry/StringEntry.php) - [Structure](../../../src/core/etl/src/Flow/ETL/Row/Entry/StructureEntry.php) - [Uuid](../../../src/core/etl/src/Flow/ETL/Row/Entry/UuidEntry.php) diff --git a/examples/topics/aggregations/first/output.txt b/examples/topics/aggregations/first/output.txt index f927339a6..d0fb05fe6 100644 --- a/examples/topics/aggregations/first/output.txt +++ b/examples/topics/aggregations/first/output.txt @@ -1,6 +1,6 @@ -+-----+ -| a | -+-----+ -| 100 | -+-----+ ++---------+ +| a_first | ++---------+ +| 100 | ++---------+ 1 rows diff --git a/examples/topics/aggregations/last/output.txt b/examples/topics/aggregations/last/output.txt index 912b1b55e..5f059c7da 100644 --- a/examples/topics/aggregations/last/output.txt +++ b/examples/topics/aggregations/last/output.txt @@ -1,6 +1,6 @@ -+-----+ -| a | -+-----+ -| 400 | -+-----+ ++--------+ +| a_last | ++--------+ +| 400 | ++--------+ 1 rows diff --git a/examples/topics/data_frame/data_frame/code.php b/examples/topics/data_frame/data_frame/code.php index 236efc3ab..778925089 100644 --- a/examples/topics/data_frame/data_frame/code.php +++ b/examples/topics/data_frame/data_frame/code.php @@ -2,13 +2,22 @@ declare(strict_types=1); -use function Flow\ETL\DSL\{array_entry, array_expand, data_frame, from_rows, int_entry, ref, row, rows, to_stream}; +use function Flow\ETL\DSL\{ + array_expand, + data_frame, + from_rows, + int_entry, + json_entry, + ref, + row, + rows, + to_stream}; data_frame() ->read( from_rows( rows( - row(int_entry('id', 1), array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), + row(int_entry('id', 1), json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), ) ) ) diff --git a/examples/topics/data_frame/rename_entries/code.php b/examples/topics/data_frame/rename_entries/code.php index 779266c3b..8a9b7b41c 100644 --- a/examples/topics/data_frame/rename_entries/code.php +++ b/examples/topics/data_frame/rename_entries/code.php @@ -8,9 +8,9 @@ data_frame() ->read(from_array([ - ['id' => 1, 'name' => 'Norbert', 'joined_id' => 1, 'joined_Status' => 'active'], - ['id' => 2, 'name' => 'John', 'joined_id' => 2, 'joined_Status' => 'inactive'], - ['id' => 3, 'name' => 'Jane', 'joined_id' => 3, 'joined_Status' => 'active'], + ['id' => 1, 'name' => 'Norbert', 'joined_id' => 1, 'joined_status' => 'active'], + ['id' => 2, 'name' => 'John', 'joined_id' => 2, 'joined_status' => 'inactive'], + ['id' => 3, 'name' => 'Jane', 'joined_id' => 3, 'joined_status' => 'active'], ])) ->rename('id', 'user_id') ->renameAll('joined_', '') diff --git a/examples/topics/data_frame/rename_entries/output.txt b/examples/topics/data_frame/rename_entries/output.txt index 3649e7ed6..6a4a76363 100644 --- a/examples/topics/data_frame/rename_entries/output.txt +++ b/examples/topics/data_frame/rename_entries/output.txt @@ -1,8 +1,8 @@ -+---------+----+----------+--------+ -| name | id | status | userId | -+---------+----+----------+--------+ -| Norbert | 1 | active | 1 | -| John | 2 | inactive | 2 | -| Jane | 3 | active | 3 | -+---------+----+----------+--------+ ++---------+----+----------+---------+ +| name | id | status | user_id | ++---------+----+----------+---------+ +| Norbert | 1 | active | 1 | +| John | 2 | inactive | 2 | +| Jane | 3 | active | 3 | ++---------+----+----------+---------+ 3 rows diff --git a/examples/topics/data_frame/reorder_entries/code.php b/examples/topics/data_frame/reorder_entries/code.php index e074e565d..fb37fd327 100644 --- a/examples/topics/data_frame/reorder_entries/code.php +++ b/examples/topics/data_frame/reorder_entries/code.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ bool_entry, compare_entries_by_type_and_name, data_frame, @@ -13,7 +13,6 @@ json_entry, list_entry, map_entry, - object_entry, row, rows, str_entry, @@ -44,13 +43,6 @@ str_entry('string_b', 'string'), uuid_entry('uuid', '06143adb-3009-45c8-a4f0-c7016f97cab7'), json_entry('json', ['id' => 1, 'status' => 'NEW']), - array_entry( - 'array', - [ - ['id' => 1, 'status' => 'NEW'], - ['id' => 2, 'status' => 'PENDING'], - ] - ), list_entry('list', [1, 2, 3], type_list(type_int())), map_entry('map', [0 => 'zero', 1 => 'one', 2 => 'two'], type_map(type_int(), type_string())), struct_entry( @@ -76,7 +68,6 @@ ), ]), ), - object_entry('object', new ArrayIterator([1, 2, 3])), ) ))) ->reorderEntries(compare_entries_by_type_and_name()) diff --git a/examples/topics/data_frame/reorder_entries/output.txt b/examples/topics/data_frame/reorder_entries/output.txt index 56dee1892..324bd1e69 100644 --- a/examples/topics/data_frame/reorder_entries/output.txt +++ b/examples/topics/data_frame/reorder_entries/output.txt @@ -1,6 +1,6 @@ -+--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+-------------------------------------------------------+---------+-------------------------+----------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| uuid | int_a | int_b | bool | bool_a | bool_c | float_a | float_b | datetime_d | datetime_z | string_a | string_b | array | list | json | map | object | struct | -+--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+-------------------------------------------------------+---------+-------------------------+----------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| 06143adb-3009-45c8-a4f0-c7016f97cab7 | 1 | 1 | false | false | false | 572.91 | 210.21 | 2024-04-01T00:00:00+00:00 | 2024-04-01T00:00:00+00:00 | string | string | [{"id":1,"status":"NEW"},{"id":2,"status":"PENDING"}] | [1,2,3] | {"id":1,"status":"NEW"} | ["zero","one","two"] | ArrayIterator Object( [storage:ArrayIterator:private] => Array ( [0] => 1 [1] => 2 [2] => 3 )) | {"street":"street","city":"city","zip":"zip","country":"country","location":{"lat":1.5,"lon":1.5}} | -+--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+-------------------------------------------------------+---------+-------------------------+----------------------+------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ ++--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+---------+-------------------------+----------------------+----------------------------------------------------------------------------------------------------+ +| uuid | int_a | int_b | bool | bool_a | bool_c | float_a | float_b | datetime_d | datetime_z | string_a | string_b | list | json | map | struct | ++--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+---------+-------------------------+----------------------+----------------------------------------------------------------------------------------------------+ +| 06143adb-3009-45c8-a4f0-c7016f97cab7 | 1 | 1 | false | false | false | 572.91 | 210.21 | 2024-04-01T00:00:00+00:00 | 2024-04-01T00:00:00+00:00 | string | string | [1,2,3] | {"id":1,"status":"NEW"} | ["zero","one","two"] | {"street":"street","city":"city","zip":"zip","country":"country","location":{"lat":1.5,"lon":1.5}} | ++--------------------------------------+-------+-------+-------+--------+--------+---------+---------+---------------------------+---------------------------+----------+----------+---------+-------------------------+----------------------+----------------------------------------------------------------------------------------------------+ 1 rows diff --git a/examples/topics/data_sink/parquet/output.parquet b/examples/topics/data_sink/parquet/output.parquet index 4d87367e9..ef449fc41 100644 Binary files a/examples/topics/data_sink/parquet/output.parquet and b/examples/topics/data_sink/parquet/output.parquet differ diff --git a/examples/topics/data_source/http_dynamic/code.php b/examples/topics/data_source/http_dynamic/code.php index ad2e50a09..2ab5741de 100644 --- a/examples/topics/data_source/http_dynamic/code.php +++ b/examples/topics/data_source/http_dynamic/code.php @@ -33,7 +33,6 @@ public function create(?Message\ResponseInterface $previousResponse = null) : ?M data_frame() ->read($from_github_api) ->withEntry('unpacked', ref('response_body')->jsonDecode()) - ->select('unpacked') ->withEntry('unpacked', ref('unpacked')->unpack()) ->renameAll('unpacked.', '') ->drop('unpacked') diff --git a/examples/topics/data_source/http_dynamic/output.txt b/examples/topics/data_source/http_dynamic/output.txt index f69eba571..52840e318 100644 --- a/examples/topics/data_source/http_dynamic/output.txt +++ b/examples/topics/data_source/http_dynamic/output.txt @@ -1,6 +1,6 @@ +----------+-----------------------------+---------------------+----------+--------------+-----------+----------------------+ | name | html_url | blog | login | public_repos | followers | created_at | +----------+-----------------------------+---------------------+----------+--------------+-----------+----------------------+ -| Flow PHP | https://github.com/flow-php | http://flow-php.com | flow-php | 34 | 98 | 2020-10-26T18:40:27Z | +| Flow PHP | https://github.com/flow-php | http://flow-php.com | flow-php | 36 | 101 | 2020-10-26T18:40:27Z | +----------+-----------------------------+---------------------+----------+--------------+-----------+----------------------+ 1 rows diff --git a/examples/topics/data_source/sequence_date/output.txt b/examples/topics/data_source/sequence_date/output.txt index 6b3d483df..616cf804d 100644 --- a/examples/topics/data_source/sequence_date/output.txt +++ b/examples/topics/data_source/sequence_date/output.txt @@ -1,65 +1,65 @@ -+---------------------------+ -| date | -+---------------------------+ -| 2024-01-01T00:00:00+00:00 | -| 2024-01-02T00:00:00+00:00 | -| 2024-01-03T00:00:00+00:00 | -| 2024-01-04T00:00:00+00:00 | -| 2024-01-05T00:00:00+00:00 | -| 2024-01-06T00:00:00+00:00 | -| 2024-01-07T00:00:00+00:00 | -| 2024-01-08T00:00:00+00:00 | -| 2024-01-09T00:00:00+00:00 | -| 2024-01-10T00:00:00+00:00 | -| 2024-01-11T00:00:00+00:00 | -| 2024-01-12T00:00:00+00:00 | -| 2024-01-13T00:00:00+00:00 | -| 2024-01-14T00:00:00+00:00 | -| 2024-01-15T00:00:00+00:00 | -| 2024-01-16T00:00:00+00:00 | -| 2024-01-17T00:00:00+00:00 | -| 2024-01-18T00:00:00+00:00 | -| 2024-01-19T00:00:00+00:00 | -| 2024-01-20T00:00:00+00:00 | -| 2024-01-21T00:00:00+00:00 | -| 2024-01-22T00:00:00+00:00 | -| 2024-01-23T00:00:00+00:00 | -| 2024-01-24T00:00:00+00:00 | -| 2024-01-25T00:00:00+00:00 | -| 2024-01-26T00:00:00+00:00 | -| 2024-01-27T00:00:00+00:00 | -| 2024-01-28T00:00:00+00:00 | -| 2024-01-29T00:00:00+00:00 | -| 2024-01-30T00:00:00+00:00 | -| 2024-01-31T00:00:00+00:00 | -| 2024-02-01T00:00:00+00:00 | -| 2024-02-02T00:00:00+00:00 | -| 2024-02-03T00:00:00+00:00 | -| 2024-02-04T00:00:00+00:00 | -| 2024-02-05T00:00:00+00:00 | -| 2024-02-06T00:00:00+00:00 | -| 2024-02-07T00:00:00+00:00 | -| 2024-02-08T00:00:00+00:00 | -| 2024-02-09T00:00:00+00:00 | -| 2024-02-10T00:00:00+00:00 | -| 2024-02-11T00:00:00+00:00 | -| 2024-02-12T00:00:00+00:00 | -| 2024-02-13T00:00:00+00:00 | -| 2024-02-14T00:00:00+00:00 | -| 2024-02-15T00:00:00+00:00 | -| 2024-02-16T00:00:00+00:00 | -| 2024-02-17T00:00:00+00:00 | -| 2024-02-18T00:00:00+00:00 | -| 2024-02-19T00:00:00+00:00 | -| 2024-02-20T00:00:00+00:00 | -| 2024-02-21T00:00:00+00:00 | -| 2024-02-22T00:00:00+00:00 | -| 2024-02-23T00:00:00+00:00 | -| 2024-02-24T00:00:00+00:00 | -| 2024-02-25T00:00:00+00:00 | -| 2024-02-26T00:00:00+00:00 | -| 2024-02-27T00:00:00+00:00 | -| 2024-02-28T00:00:00+00:00 | -| 2024-02-29T00:00:00+00:00 | -+---------------------------+ ++------------+ +| date | ++------------+ +| 2024-01-01 | +| 2024-01-02 | +| 2024-01-03 | +| 2024-01-04 | +| 2024-01-05 | +| 2024-01-06 | +| 2024-01-07 | +| 2024-01-08 | +| 2024-01-09 | +| 2024-01-10 | +| 2024-01-11 | +| 2024-01-12 | +| 2024-01-13 | +| 2024-01-14 | +| 2024-01-15 | +| 2024-01-16 | +| 2024-01-17 | +| 2024-01-18 | +| 2024-01-19 | +| 2024-01-20 | +| 2024-01-21 | +| 2024-01-22 | +| 2024-01-23 | +| 2024-01-24 | +| 2024-01-25 | +| 2024-01-26 | +| 2024-01-27 | +| 2024-01-28 | +| 2024-01-29 | +| 2024-01-30 | +| 2024-01-31 | +| 2024-02-01 | +| 2024-02-02 | +| 2024-02-03 | +| 2024-02-04 | +| 2024-02-05 | +| 2024-02-06 | +| 2024-02-07 | +| 2024-02-08 | +| 2024-02-09 | +| 2024-02-10 | +| 2024-02-11 | +| 2024-02-12 | +| 2024-02-13 | +| 2024-02-14 | +| 2024-02-15 | +| 2024-02-16 | +| 2024-02-17 | +| 2024-02-18 | +| 2024-02-19 | +| 2024-02-20 | +| 2024-02-21 | +| 2024-02-22 | +| 2024-02-23 | +| 2024-02-24 | +| 2024-02-25 | +| 2024-02-26 | +| 2024-02-27 | +| 2024-02-28 | +| 2024-02-29 | ++------------+ 60 rows diff --git a/examples/topics/data_source/sequence_date_recurrences/output.txt b/examples/topics/data_source/sequence_date_recurrences/output.txt index 206c0344e..182408165 100644 --- a/examples/topics/data_source/sequence_date_recurrences/output.txt +++ b/examples/topics/data_source/sequence_date_recurrences/output.txt @@ -1,66 +1,66 @@ -+---------------------------+ -| date | -+---------------------------+ -| 2024-01-01T00:00:00+00:00 | -| 2024-01-02T00:00:00+00:00 | -| 2024-01-03T00:00:00+00:00 | -| 2024-01-04T00:00:00+00:00 | -| 2024-01-05T00:00:00+00:00 | -| 2024-01-06T00:00:00+00:00 | -| 2024-01-07T00:00:00+00:00 | -| 2024-01-08T00:00:00+00:00 | -| 2024-01-09T00:00:00+00:00 | -| 2024-01-10T00:00:00+00:00 | -| 2024-01-11T00:00:00+00:00 | -| 2024-01-12T00:00:00+00:00 | -| 2024-01-13T00:00:00+00:00 | -| 2024-01-14T00:00:00+00:00 | -| 2024-01-15T00:00:00+00:00 | -| 2024-01-16T00:00:00+00:00 | -| 2024-01-17T00:00:00+00:00 | -| 2024-01-18T00:00:00+00:00 | -| 2024-01-19T00:00:00+00:00 | -| 2024-01-20T00:00:00+00:00 | -| 2024-01-21T00:00:00+00:00 | -| 2024-01-22T00:00:00+00:00 | -| 2024-01-23T00:00:00+00:00 | -| 2024-01-24T00:00:00+00:00 | -| 2024-01-25T00:00:00+00:00 | -| 2024-01-26T00:00:00+00:00 | -| 2024-01-27T00:00:00+00:00 | -| 2024-01-28T00:00:00+00:00 | -| 2024-01-29T00:00:00+00:00 | -| 2024-01-30T00:00:00+00:00 | -| 2024-01-31T00:00:00+00:00 | -| 2024-02-01T00:00:00+00:00 | -| 2024-02-02T00:00:00+00:00 | -| 2024-02-03T00:00:00+00:00 | -| 2024-02-04T00:00:00+00:00 | -| 2024-02-05T00:00:00+00:00 | -| 2024-02-06T00:00:00+00:00 | -| 2024-02-07T00:00:00+00:00 | -| 2024-02-08T00:00:00+00:00 | -| 2024-02-09T00:00:00+00:00 | -| 2024-02-10T00:00:00+00:00 | -| 2024-02-11T00:00:00+00:00 | -| 2024-02-12T00:00:00+00:00 | -| 2024-02-13T00:00:00+00:00 | -| 2024-02-14T00:00:00+00:00 | -| 2024-02-15T00:00:00+00:00 | -| 2024-02-16T00:00:00+00:00 | -| 2024-02-17T00:00:00+00:00 | -| 2024-02-18T00:00:00+00:00 | -| 2024-02-19T00:00:00+00:00 | -| 2024-02-20T00:00:00+00:00 | -| 2024-02-21T00:00:00+00:00 | -| 2024-02-22T00:00:00+00:00 | -| 2024-02-23T00:00:00+00:00 | -| 2024-02-24T00:00:00+00:00 | -| 2024-02-25T00:00:00+00:00 | -| 2024-02-26T00:00:00+00:00 | -| 2024-02-27T00:00:00+00:00 | -| 2024-02-28T00:00:00+00:00 | -| 2024-02-29T00:00:00+00:00 | -| 2024-03-01T00:00:00+00:00 | -+---------------------------+ ++------------+ +| date | ++------------+ +| 2024-01-01 | +| 2024-01-02 | +| 2024-01-03 | +| 2024-01-04 | +| 2024-01-05 | +| 2024-01-06 | +| 2024-01-07 | +| 2024-01-08 | +| 2024-01-09 | +| 2024-01-10 | +| 2024-01-11 | +| 2024-01-12 | +| 2024-01-13 | +| 2024-01-14 | +| 2024-01-15 | +| 2024-01-16 | +| 2024-01-17 | +| 2024-01-18 | +| 2024-01-19 | +| 2024-01-20 | +| 2024-01-21 | +| 2024-01-22 | +| 2024-01-23 | +| 2024-01-24 | +| 2024-01-25 | +| 2024-01-26 | +| 2024-01-27 | +| 2024-01-28 | +| 2024-01-29 | +| 2024-01-30 | +| 2024-01-31 | +| 2024-02-01 | +| 2024-02-02 | +| 2024-02-03 | +| 2024-02-04 | +| 2024-02-05 | +| 2024-02-06 | +| 2024-02-07 | +| 2024-02-08 | +| 2024-02-09 | +| 2024-02-10 | +| 2024-02-11 | +| 2024-02-12 | +| 2024-02-13 | +| 2024-02-14 | +| 2024-02-15 | +| 2024-02-16 | +| 2024-02-17 | +| 2024-02-18 | +| 2024-02-19 | +| 2024-02-20 | +| 2024-02-21 | +| 2024-02-22 | +| 2024-02-23 | +| 2024-02-24 | +| 2024-02-25 | +| 2024-02-26 | +| 2024-02-27 | +| 2024-02-28 | +| 2024-02-29 | +| 2024-03-01 | ++------------+ 61 rows diff --git a/examples/topics/transformations/array_expand/code.php b/examples/topics/transformations/array_expand/code.php index e02b2b753..dd7954c88 100644 --- a/examples/topics/transformations/array_expand/code.php +++ b/examples/topics/transformations/array_expand/code.php @@ -2,11 +2,12 @@ declare(strict_types=1); -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ array_expand, data_frame, from_rows, int_entry, + json_entry, ref, row, rows, @@ -16,7 +17,7 @@ data_frame() ->read(from_rows(rows( - row(int_entry('id', 1), array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), + row(int_entry('id', 1), json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), ))) ->withEntry('expanded', array_expand(ref('array'))) ->write(to_stream(__DIR__ . '/output.txt', truncate: false)) diff --git a/examples/topics/transformations/array_unpack/code.php b/examples/topics/transformations/array_unpack/code.php index 721922e50..370ba4ee9 100644 --- a/examples/topics/transformations/array_unpack/code.php +++ b/examples/topics/transformations/array_unpack/code.php @@ -2,14 +2,14 @@ declare(strict_types=1); -use function Flow\ETL\DSL\{array_entry, data_frame, from_rows, int_entry, ref, row, rows, to_stream}; +use function Flow\ETL\DSL\{data_frame, from_rows, int_entry, json_entry, ref, row, rows, to_stream}; require __DIR__ . '/../../../autoload.php'; data_frame() ->read(from_rows(rows( - row(int_entry('id', 1), array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), - row(int_entry('id', 2), array_entry('array', ['d' => 4, 'e' => 5, 'f' => 6])), + row(int_entry('id', 1), json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), + row(int_entry('id', 2), json_entry('array', ['d' => 4, 'e' => 5, 'f' => 6])), ))) ->withEntry('unpacked', ref('array')->unpack()) ->write(to_stream(__DIR__ . '/output.txt', truncate: false)) diff --git a/examples/topics/transformations/size/code.php b/examples/topics/transformations/size/code.php index 01dca631d..52fcffd40 100644 --- a/examples/topics/transformations/size/code.php +++ b/examples/topics/transformations/size/code.php @@ -2,13 +2,13 @@ declare(strict_types=1); -use function Flow\ETL\DSL\{array_entry, data_frame, from_rows, int_entry, ref, row, rows, to_stream}; +use function Flow\ETL\DSL\{data_frame, from_rows, int_entry, json_entry, ref, row, rows, to_stream}; require __DIR__ . '/../../../autoload.php'; data_frame() ->read(from_rows(rows( - row(int_entry('id', 1), array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), + row(int_entry('id', 1), json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3])), ))) ->withEntry('array_size', ref('array')->size()) ->write(to_stream(__DIR__ . '/output.txt', truncate: false)) diff --git a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php index d8881bca7..83b89c9ca 100644 --- a/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php @@ -24,12 +24,10 @@ public function normalize(Entry $entry) : string|float|int|bool|null Entry\XMLEntry::class => $entry->toString(), Entry\DateTimeEntry::class => $entry->value()?->format($this->dateTimeFormat), Entry\EnumEntry::class => $entry->value()?->name, - Entry\ArrayEntry::class, Entry\ListEntry::class, Entry\MapEntry::class, Entry\StructureEntry::class, - Entry\JsonEntry::class, - Entry\ObjectEntry::class => $this->caster->to(type_json())->value($entry->value()), + Entry\JsonEntry::class => $this->caster->to(type_json())->value($entry->value()), default => $entry->value(), }; } diff --git a/src/adapter/etl-adapter-elasticsearch/src/Flow/ETL/Adapter/Elasticsearch/ElasticsearchPHP/ElasticsearchLoader.php b/src/adapter/etl-adapter-elasticsearch/src/Flow/ETL/Adapter/Elasticsearch/ElasticsearchPHP/ElasticsearchLoader.php index d5a3e751c..ed3d72a2d 100644 --- a/src/adapter/etl-adapter-elasticsearch/src/Flow/ETL/Adapter/Elasticsearch/ElasticsearchPHP/ElasticsearchLoader.php +++ b/src/adapter/etl-adapter-elasticsearch/src/Flow/ETL/Adapter/Elasticsearch/ElasticsearchPHP/ElasticsearchLoader.php @@ -70,17 +70,7 @@ public function load(Rows $rows, FlowContext $context) : void */ $dataCollection = $rows->map(fn (Row $row) : Row => Row::create( $factory->create($row), - new Row\Entry\ArrayEntry('body', $row->map( - function (Row\Entry $entry) : Row\Entry { - $entryValue = $entry->value(); - - if ($entry instanceof Row\Entry\JsonEntry && $entryValue !== null) { - return new Row\Entry\ArrayEntry($entry->name(), (array) \json_decode($entryValue, true, 512, JSON_THROW_ON_ERROR)); - } - - return $entry; - } - )->toArray()) + new Row\Entry\JsonEntry('body', $row->toArray()) ))->toArray(); foreach ($dataCollection as $data) { diff --git a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientDynamicExtractor.php b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientDynamicExtractor.php index 2d9434d3e..b0fff9121 100644 --- a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientDynamicExtractor.php +++ b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientDynamicExtractor.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Adapter\Http; -use function Flow\ETL\DSL\{array_entry, str_entry}; +use function Flow\ETL\DSL\{json_entry, str_entry}; use Flow\ETL\Adapter\Http\DynamicExtractor\NextRequestFactory; use Flow\ETL\{Extractor, FlowContext, Row, Rows}; use Psr\Http\Client\ClientInterface; @@ -61,7 +61,7 @@ public function extract(FlowContext $context) : \Generator [ str_entry('request_uri', (string) $nextRequest->getUri()), str_entry('request_method', $nextRequest->getMethod()), - array_entry('request_headers', $nextRequest->getHeaders()), + json_entry('request_headers', $nextRequest->getHeaders()), ] ) ) diff --git a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientStaticExtractor.php b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientStaticExtractor.php index 4d6bd27c1..f3c2475c8 100644 --- a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientStaticExtractor.php +++ b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/PsrHttpClientStaticExtractor.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Adapter\Http; -use function Flow\ETL\DSL\{array_entry, str_entry}; +use function Flow\ETL\DSL\{json_entry, str_entry}; use Flow\ETL\{Extractor, FlowContext, Row, Rows}; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\{RequestInterface, ResponseInterface}; @@ -57,7 +57,7 @@ public function extract(FlowContext $context) : \Generator [ str_entry('request_uri', (string) $request->getUri()), str_entry('request_method', $request->getMethod()), - array_entry('request_headers', $request->getHeaders()), + json_entry('request_headers', $request->getHeaders()), ] ) ) diff --git a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/RequestEntriesFactory.php b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/RequestEntriesFactory.php index 76b82c036..8de5bc346 100644 --- a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/RequestEntriesFactory.php +++ b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/RequestEntriesFactory.php @@ -74,7 +74,7 @@ public function create(RequestInterface $request) : Row\Entries return new Row\Entries( $requestBodyEntry, new Row\Entry\StringEntry('request_uri', (string) $request->getUri()), - new Row\Entry\ArrayEntry('request_headers', $request->getHeaders()), + new Row\Entry\JsonEntry('request_headers', $request->getHeaders()), new Row\Entry\StringEntry('request_protocol_version', $request->getProtocolVersion()), new Row\Entry\StringEntry('request_method', $request->getMethod()), ); diff --git a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/ResponseEntriesFactory.php b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/ResponseEntriesFactory.php index ce12f94cf..2e76aa557 100644 --- a/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/ResponseEntriesFactory.php +++ b/src/adapter/etl-adapter-http/src/Flow/ETL/Adapter/Http/ResponseEntriesFactory.php @@ -64,7 +64,7 @@ public function create(ResponseInterface $response) : Row\Entries return new Row\Entries( $responseBodyEntry, - new Row\Entry\ArrayEntry('response_headers', $response->getHeaders()), + new Row\Entry\JsonEntry('response_headers', $response->getHeaders()), new Row\Entry\IntegerEntry('response_status_code', $response->getStatusCode()), new Row\Entry\StringEntry('response_protocol_version', $response->getProtocolVersion()), new Row\Entry\StringEntry('response_reason_phrase', $response->getReasonPhrase()), diff --git a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/JsonLoader.php b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/JsonLoader.php index 7c3fd2639..eb03b38e1 100644 --- a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/JsonLoader.php +++ b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/JsonLoader.php @@ -76,7 +76,7 @@ public function withRowsInNewLines(bool $putRowsInNewLines) : self public function write(Rows $nextRows, array $partitions, FlowContext $context) : void { $streams = $context->streams(); - $normalizer = new RowsNormalizer(new EntryNormalizer($context->config->caster(), $this->dateTimeFormat)); + $normalizer = new RowsNormalizer(new EntryNormalizer($this->dateTimeFormat)); if (!$streams->isOpen($this->path, $partitions)) { $stream = $streams->writeTo($this->path, $partitions); diff --git a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php index d132f7f9e..82ea30065 100644 --- a/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php @@ -4,14 +4,11 @@ namespace Flow\ETL\Adapter\JSON\RowsNormalizer; -use function Flow\ETL\DSL\type_array; -use Flow\ETL\PHP\Type\Caster; use Flow\ETL\Row\Entry; final class EntryNormalizer { public function __construct( - private readonly Caster $caster, private readonly string $dateTimeFormat = \DateTimeInterface::ATOM, ) { } @@ -22,12 +19,10 @@ public function normalize(Entry $entry) : string|float|int|bool|array|null Entry\UuidEntry::class => $entry->toString(), Entry\DateTimeEntry::class => $entry->value()?->format($this->dateTimeFormat), Entry\EnumEntry::class => $entry->value()?->name, - Entry\ArrayEntry::class, Entry\ListEntry::class, Entry\MapEntry::class, Entry\StructureEntry::class, Entry\JsonEntry::class, - Entry\ObjectEntry::class => $this->caster->to(type_array())->value($entry->value()), Entry\XMLElementEntry::class => $entry->toString(), Entry\XMLEntry::class => $entry->toString(), default => $entry->value(), diff --git a/src/adapter/etl-adapter-meilisearch/src/Flow/ETL/Adapter/Meilisearch/MeilisearchPHP/MeilisearchLoader.php b/src/adapter/etl-adapter-meilisearch/src/Flow/ETL/Adapter/Meilisearch/MeilisearchPHP/MeilisearchLoader.php index c8cbad3f0..a31e88a69 100644 --- a/src/adapter/etl-adapter-meilisearch/src/Flow/ETL/Adapter/Meilisearch/MeilisearchPHP/MeilisearchLoader.php +++ b/src/adapter/etl-adapter-meilisearch/src/Flow/ETL/Adapter/Meilisearch/MeilisearchPHP/MeilisearchLoader.php @@ -38,12 +38,6 @@ public function load(Rows $rows, FlowContext $context) : void $dataCollection = $rows->map(fn (Row $row) : Row => Row::create( ...$row->map( function (Row\Entry $entry) : Row\Entry { - $entryValue = $entry->value(); - - if ($entry instanceof Row\Entry\JsonEntry && $entryValue !== null) { - return new Row\Entry\ArrayEntry($entry->name(), (array) \json_decode($entryValue, true, 512, JSON_THROW_ON_ERROR)); - } - return $entry; } )->entries() diff --git a/src/adapter/etl-adapter-parquet/src/Flow/ETL/Adapter/Parquet/SchemaConverter.php b/src/adapter/etl-adapter-parquet/src/Flow/ETL/Adapter/Parquet/SchemaConverter.php index 8db514f18..a20bc2e42 100644 --- a/src/adapter/etl-adapter-parquet/src/Flow/ETL/Adapter/Parquet/SchemaConverter.php +++ b/src/adapter/etl-adapter-parquet/src/Flow/ETL/Adapter/Parquet/SchemaConverter.php @@ -5,25 +5,34 @@ namespace Flow\ETL\Adapter\Parquet; use function Flow\ETL\DSL\{bool_schema, + date_schema, datetime_schema, float_schema, int_schema, json_schema, list_schema, map_schema, - object_schema, str_schema, struct_schema, struct_type, structure_element, + time_schema, type_list, type_map, - type_object, uuid_schema}; use Flow\ETL\Exception\RuntimeException; use Flow\ETL\PHP\Type\Logical\Map\{MapKey, MapValue}; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; -use Flow\ETL\PHP\Type\Logical\{DateTimeType, JsonType, ListType, MapType, StructureType, UuidType, XMLElementType, XMLType}; +use Flow\ETL\PHP\Type\Logical\{DateTimeType, + DateType, + JsonType, + ListType, + MapType, + StructureType, + TimeType, + UuidType, + XMLElementType, + XMLType}; use Flow\ETL\PHP\Type\Native\{ObjectType, ScalarType}; use Flow\ETL\PHP\Type\Type; use Flow\ETL\PHP\Value\Uuid; @@ -78,6 +87,10 @@ private function flowListToParquetList(ListType $type) : ListElement break; case DateTimeType::class: return ListElement::datetime(!$element->nullable()); + case DateType::class: + return ListElement::date(!$element->nullable()); + case TimeType::class: + return ListElement::time(!$element->nullable()); case UuidType::class: return ListElement::uuid(!$element->nullable()); case JsonType::class: @@ -117,6 +130,10 @@ private function flowMapKeyToParquetMapKey(MapKey $mapKey) : ParquetSchema\MapKe return ParquetSchema\MapKey::uuid(); case DateTimeType::class: return ParquetSchema\MapKey::datetime(); + case DateType::class: + return ParquetSchema\MapKey::date(); + case TimeType::class: + return ParquetSchema\MapKey::time(); case ScalarType::class: switch ($mapKeyType->type()) { case ScalarType::FLOAT: @@ -155,6 +172,10 @@ private function flowMapValueToParquetMapValue(MapValue $mapValue) : ParquetSche break; case UuidType::class: return ParquetSchema\MapValue::uuid(!$mapValueType->nullable()); + case DateType::class: + return ParquetSchema\MapValue::date(!$mapValueType->nullable()); + case TimeType::class: + return ParquetSchema\MapValue::time(!$mapValueType->nullable()); case DateTimeType::class: return ParquetSchema\MapValue::datetime(!$mapValueType->nullable()); case JsonType::class: @@ -237,6 +258,10 @@ private function flowTypeToParquetType(string $name, Type $type) : Column switch ($type::class) { case ScalarType::class: return $this->flowScalarToParquetFlat($type, $name); + case TimeType::class: + return FlatColumn::time($name, $type->nullable() ? ParquetSchema\Repetition::OPTIONAL : ParquetSchema\Repetition::REQUIRED); + case DateType::class: + return FlatColumn::date($name, $type->nullable() ? ParquetSchema\Repetition::OPTIONAL : ParquetSchema\Repetition::REQUIRED); case DateTimeType::class: return FlatColumn::datetime($name, $type->nullable() ? ParquetSchema\Repetition::OPTIONAL : ParquetSchema\Repetition::REQUIRED); case UuidType::class: @@ -283,7 +308,7 @@ private function parquetFlatToFlowType(FlatColumn $column) : Schema\Definition if ($logicalType === null) { return match ($column->type()) { ParquetSchema\PhysicalType::INT32 => match ($column->convertedType()) { - ParquetSchema\ConvertedType::DATE => datetime_schema($column->name(), $nullable), + ParquetSchema\ConvertedType::DATE => date_schema($column->name(), $nullable), default => int_schema($column->name(), $nullable), }, ParquetSchema\PhysicalType::INT64 => int_schema($column->name(), $nullable), @@ -297,8 +322,8 @@ private function parquetFlatToFlowType(FlatColumn $column) : Schema\Definition return match ($logicalType->name()) { ParquetSchema\LogicalType::STRING => str_schema($column->name(), $nullable), - ParquetSchema\LogicalType::DATE => datetime_schema($column->name(), $nullable), - ParquetSchema\LogicalType::TIME => object_schema($column->name(), type_object(\DateInterval::class, $nullable)), + ParquetSchema\LogicalType::TIME => time_schema($column->name(), $nullable), + ParquetSchema\LogicalType::DATE => date_schema($column->name(), $nullable), ParquetSchema\LogicalType::TIMESTAMP => datetime_schema($column->name(), $nullable), ParquetSchema\LogicalType::UUID => uuid_schema($column->name(), $nullable), ParquetSchema\LogicalType::JSON => json_schema($column->name(), $nullable), diff --git a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Integration/ParquetTest.php b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Integration/ParquetTest.php index f3a29bb4e..d30bbeb13 100644 --- a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Integration/ParquetTest.php +++ b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Integration/ParquetTest.php @@ -66,8 +66,8 @@ public function test_writing_with_provided_schema() : void self::assertEquals( [ - ['id' => '1', 'name' => 'test', 'uuid' => new \Flow\ETL\PHP\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => '[{"id":1,"name":"test"},{"id":2,"name":"test"}]'], - ['id' => '2', 'name' => 'test', 'uuid' => new \Flow\ETL\PHP\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => '[{"id":1,"name":"test"},{"id":2,"name":"test"}]'], + ['id' => '1', 'name' => 'test', 'uuid' => new \Flow\ETL\PHP\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]], + ['id' => '2', 'name' => 'test', 'uuid' => new \Flow\ETL\PHP\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]], ], df() ->read(from_parquet($path)) diff --git a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/FlowToParquetSchemaTest.php b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/FlowToParquetSchemaTest.php index d8ac98df3..8248b7b1b 100644 --- a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/FlowToParquetSchemaTest.php +++ b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/FlowToParquetSchemaTest.php @@ -4,9 +4,8 @@ namespace Flow\ETL\Adapter\Parquet\Tests\Unit; -use function Flow\ETL\DSL\{type_boolean, type_int, type_object, type_string}; +use function Flow\ETL\DSL\{type_boolean, type_int, type_string}; use Flow\ETL\Adapter\Parquet\SchemaConverter; -use Flow\ETL\Exception\RuntimeException; use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\Map\{MapKey, MapValue}; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; @@ -18,16 +17,6 @@ final class FlowToParquetSchemaTest extends TestCase { - public function test_convert_array_entry_to_parquet_array() : void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage("Flow\ETL\PHP\Type\Native\ArrayType is not supported."); - - (new SchemaConverter())->toParquet(new Schema( - Schema\Definition::array('array') - )); - } - public function test_convert_etl_entries_to_parquet_fields() : void { self::assertEquals( @@ -48,7 +37,6 @@ public function test_convert_etl_entries_to_parquet_fields() : void ), ParquetSchema\Repetition::REQUIRED), NestedColumn::struct('structure', [FlatColumn::string('a', ParquetSchema\Repetition::REQUIRED)], ParquetSchema\Repetition::REQUIRED), NestedColumn::map('map', ParquetSchema\MapKey::string(), ParquetSchema\MapValue::int64(true), ParquetSchema\Repetition::REQUIRED), - FlatColumn::time('time', ParquetSchema\Repetition::REQUIRED) ), (new SchemaConverter())->toParquet(new Schema( Schema\Definition::integer('integer'), @@ -66,18 +54,7 @@ public function test_convert_etl_entries_to_parquet_fields() : void ))), Schema\Definition::structure('structure', new StructureType([new StructureElement('a', type_string())])), Schema\Definition::map('map', new MapType(MapKey::string(), MapValue::integer())), - Schema\Definition::object('time', type_object(\DateInterval::class)) )) ); } - - public function test_convert_object_entry_to_parquet_array() : void - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage("object can't be converted to any parquet columns."); - - (new SchemaConverter())->toParquet(new Schema( - Schema\Definition::object('object', type_object(\stdClass::class)) - )); - } } diff --git a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/ParquetToFlowSchemaTest.php b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/ParquetToFlowSchemaTest.php index a941333a5..2fa25e478 100644 --- a/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/ParquetToFlowSchemaTest.php +++ b/src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Unit/ParquetToFlowSchemaTest.php @@ -4,7 +4,26 @@ namespace Flow\ETL\Adapter\Parquet\Tests\Unit; -use function Flow\ETL\DSL\{bool_schema, datetime_schema, float_schema, int_schema, json_schema, list_schema, map_schema, object_schema, str_schema, struct_element, struct_schema, type_boolean, type_int, type_list, type_map, type_object, type_string, type_structure, type_uuid, uuid_schema}; +use function Flow\ETL\DSL\{bool_schema, + date_schema, + datetime_schema, + float_schema, + int_schema, + json_schema, + list_schema, + map_schema, + str_schema, + struct_element, + struct_schema, + time_schema, + type_boolean, + type_int, + type_list, + type_map, + type_string, + type_structure, + type_uuid, + uuid_schema}; use Flow\ETL\Adapter\Parquet\SchemaConverter; use Flow\Parquet\ParquetFile\Schema; use Flow\Parquet\ParquetFile\Schema\{MapKey, MapValue}; @@ -40,8 +59,8 @@ public function test_converting_flat_fields_to_flow_schema() : void float_schema('double', true), float_schema('decimal', true), bool_schema('boolean', true), - datetime_schema('date', true), - object_schema('time', type_object(\DateInterval::class, true)), + date_schema('date', true), + time_schema('time', true), datetime_schema('datetime', true), uuid_schema('uuid', true), json_schema('json', true), diff --git a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php index 72261a789..b4f895b17 100644 --- a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php @@ -10,7 +10,7 @@ use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; use Flow\ETL\PHP\Type\Logical\{ListType, MapType, StructureType}; use Flow\ETL\Row\Entry; -use Flow\ETL\Row\Entry\{ArrayEntry, BooleanEntry, DateTimeEntry, EnumEntry, FloatEntry, IntegerEntry, JsonEntry, ListEntry, MapEntry, ObjectEntry, StringEntry, StructureEntry, UuidEntry}; +use Flow\ETL\Row\Entry\{BooleanEntry, DateTimeEntry, EnumEntry, FloatEntry, IntegerEntry, JsonEntry, ListEntry, MapEntry, StringEntry, StructureEntry, UuidEntry}; final class EntryNormalizer { @@ -45,10 +45,8 @@ public function normalize(Entry $entry) : XMLNode|XMLAttribute BooleanEntry::class => XMLNode::flatNode($entry->name(), $entry->value() ? 'true' : 'false'), DateTimeEntry::class => XMLNode::flatNode($entry->name(), $entry->value()?->format($this->valueNormalizer->dateTimeFormat)), EnumEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), - JsonEntry::class => XMLNode::flatNode($entry->name(), $entry->value()), + JsonEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), UuidEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), - ObjectEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), - ArrayEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), default => throw new InvalidArgumentException("Given entry type can't be converted to node, given entry type: " . $entry::class), }; } diff --git a/src/cli/tests/Flow/CLI/Tests/Integration/FileSchemaCommandTest.php b/src/cli/tests/Flow/CLI/Tests/Integration/FileSchemaCommandTest.php index 5d7884abd..8685fd208 100644 --- a/src/cli/tests/Flow/CLI/Tests/Integration/FileSchemaCommandTest.php +++ b/src/cli/tests/Flow/CLI/Tests/Integration/FileSchemaCommandTest.php @@ -149,9 +149,9 @@ public function test_run_schema_with_table_output_and_auto_cast() : void | created_at | datetime | false | | [] | | updated_at | datetime | false | | [] | | discount | scalar | true | string | [] | -| address | json | false | | [] | -| notes | json | false | | [] | -| items | json | false | | [] | +| address | map | false | | [] | +| notes | list | false | | [] | +| items | list | false | | [] | +------------+----------+----------+-------------+----------+ 7 rows @@ -177,9 +177,9 @@ public function test_run_schema_with_table_output_and_limit_5() : void | created_at | datetime | false | | [] | | updated_at | datetime | false | | [] | | discount | scalar | true | string | [] | -| address | json | false | | [] | -| notes | json | false | | [] | -| items | json | false | | [] | +| address | map | false | | [] | +| notes | list | false | | [] | +| items | list | false | | [] | +------------+----------+----------+-------------+----------+ 7 rows diff --git a/src/core/etl/src/Flow/ETL/DSL/functions.php b/src/core/etl/src/Flow/ETL/DSL/functions.php index 5476cacac..1de8a5a8b 100644 --- a/src/core/etl/src/Flow/ETL/DSL/functions.php +++ b/src/core/etl/src/Flow/ETL/DSL/functions.php @@ -81,11 +81,14 @@ use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\Map\{MapKey, MapValue}; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; -use Flow\ETL\PHP\Type\Logical\{DateTimeType, +use Flow\ETL\PHP\Type\Logical\{ + DateTimeType, + DateType, JsonType, ListType, MapType, StructureType, + TimeType, UuidType, XMLElementType, XMLType}; @@ -327,15 +330,6 @@ function to_branch(ScalarFunction $condition, Loader $loader) : Loader return new Loader\BranchingLoader($condition, $loader); } -/** - * @param array $data - */ -#[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] -function array_entry(string $array, ?array $data) : Entry\ArrayEntry -{ - return new Entry\ArrayEntry($array, $data); -} - #[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] function bool_entry(string $name, ?bool $value) : Entry\BooleanEntry { @@ -354,6 +348,18 @@ function datetime_entry(string $name, \DateTimeInterface|string|null $value) : E return new Entry\DateTimeEntry($name, $value); } +#[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] +function time_entry(string $name, \DateInterval|string|null $value) : Entry\TimeEntry +{ + return new Entry\TimeEntry($name, $value); +} + +#[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] +function date_entry(string $name, \DateTimeInterface|string|null $value) : Entry\DateEntry +{ + return new Entry\DateEntry($name, $value); +} + #[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] function int_entry(string $name, ?int $value) : Entry\IntegerEntry { @@ -397,18 +403,6 @@ function json_object_entry(string $name, array|string|null $data) : Entry\JsonEn return Entry\JsonEntry::object($name, $data); } -#[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] -function object_entry(string $name, ?object $data) : Entry\ObjectEntry -{ - return new Entry\ObjectEntry($name, $data); -} - -#[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] -function obj_entry(string $name, ?object $data) : Entry\ObjectEntry -{ - return object_entry($name, $data); -} - #[DocumentationDSL(module: Module::CORE, type: DSLType::ENTRY)] function str_entry(string $name, ?string $value) : Entry\StringEntry { @@ -532,6 +526,18 @@ function type_datetime(bool $nullable = false) : DateTimeType return new DateTimeType($nullable); } +#[DocumentationDSL(module: Module::CORE, type: DSLType::TYPE)] +function type_date(bool $nullable = false) : DateType +{ + return new DateType($nullable); +} + +#[DocumentationDSL(module: Module::CORE, type: DSLType::TYPE)] +function type_time(bool $nullable = false) : TimeType +{ + return new TimeType($nullable); +} + #[DocumentationDSL(module: Module::CORE, type: DSLType::TYPE)] function type_xml(bool $nullable = false) : XMLType { @@ -1043,6 +1049,15 @@ function number_format(ScalarFunction|int|float $value, ScalarFunction|int $deci return new NumberFormat($value, $decimals, $decimal_separator, $thousands_separator); } +/** + * @param array $data + */ +#[DocumentationDSL(module: Module::CORE, type: DSLType::DATA_FRAME)] +function to_entry(string $name, mixed $data, EntryFactory $entryFactory = new NativeEntryFactory()) : Entry +{ + return $entryFactory->create($name, $data); +} + /** * @param array>|array $data * @param array|Partitions $partitions @@ -1254,18 +1269,6 @@ function float_schema(string $name, bool $nullable = false, ?Schema\Metadata $me return Definition::float($name, $nullable, $metadata); } -#[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] -function array_schema(string $name, bool $empty = false, bool $nullable = false, ?Schema\Metadata $metadata = null) : Definition -{ - return Definition::array($name, $empty, $nullable, $metadata); -} - -#[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] -function object_schema(string $name, ObjectType $type, ?Schema\Metadata $metadata = null) : Definition -{ - return Definition::object($name, $type, $metadata); -} - #[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] function map_schema(string $name, MapType $type, ?Schema\Metadata $metadata = null) : Definition { @@ -1299,6 +1302,18 @@ function datetime_schema(string $name, bool $nullable = false, ?Schema\Metadata return Definition::datetime($name, $nullable, $metadata); } +#[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] +function time_schema(string $name, bool $nullable = false, ?Schema\Metadata $metadata = null) : Definition +{ + return Definition::time($name, $nullable, $metadata); +} + +#[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] +function date_schema(string $name, bool $nullable = false, ?Schema\Metadata $metadata = null) : Definition +{ + return Definition::date($name, $nullable, $metadata); +} + #[DocumentationDSL(module: Module::CORE, type: DSLType::SCHEMA)] function json_schema(string $name, bool $nullable = false, ?Schema\Metadata $metadata = null) : Definition { @@ -1541,3 +1556,51 @@ function dom_element_to_string(\DOMElement $element, bool $format_output = false return $doc->saveXML($doc->documentElement); } + +function date_interval_to_milliseconds(\DateInterval $interval) : int +{ + if ($interval->y !== 0 || $interval->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to milliseconds. Given" . \json_encode($interval, JSON_THROW_ON_ERROR)); + } + + $absoluteSeconds = $interval->d * 24 * 60 * 60 + + $interval->h * 60 * 60 + + $interval->i * 60 + + $interval->s; + + return $interval->invert + ? -(int) ($absoluteSeconds * 1000 + $interval->f * 1000) + : (int) ($absoluteSeconds * 1000 + $interval->f * 1000); +} + +function date_interval_to_seconds(\DateInterval $interval) : int +{ + if ($interval->y !== 0 || $interval->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to seconds. Given" . \json_encode($interval, JSON_THROW_ON_ERROR)); + } + + $absoluteSeconds = $interval->d * 24 * 60 * 60 + + $interval->h * 60 * 60 + + $interval->i * 60 + + $interval->s; + + return $interval->invert + ? -(int) ceil($absoluteSeconds + $interval->f) + : (int) ceil($absoluteSeconds + $interval->f); +} + +function date_interval_to_microseconds(\DateInterval $interval) : int +{ + if ($interval->y !== 0 || $interval->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to microseconds. Given" . \json_encode($interval, JSON_THROW_ON_ERROR)); + } + + $absoluteSeconds = $interval->d * 24 * 60 * 60 + + $interval->h * 60 * 60 + + $interval->i * 60 + + $interval->s; + + return $interval->invert + ? -(int) ($absoluteSeconds * 1000000 + $interval->f * 1000000) + : (int) ($absoluteSeconds * 1000000 + $interval->f * 1000000); +} diff --git a/src/core/etl/src/Flow/ETL/Extractor/PathPartitionsExtractor.php b/src/core/etl/src/Flow/ETL/Extractor/PathPartitionsExtractor.php index 6e975338b..976a10172 100644 --- a/src/core/etl/src/Flow/ETL/Extractor/PathPartitionsExtractor.php +++ b/src/core/etl/src/Flow/ETL/Extractor/PathPartitionsExtractor.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Extractor; -use function Flow\ETL\DSL\{array_entry, row, rows, string_entry}; +use function Flow\ETL\DSL\{map_entry, row, rows, string_entry, type_map, type_string}; use Flow\ETL\{Extractor, FlowContext}; use Flow\Filesystem\{Partition, Path}; @@ -24,7 +24,7 @@ public function extract(FlowContext $context) : \Generator $row = row( string_entry('path', $fileStatus->path->uri()), - array_entry('partitions', \array_merge(...\array_values(\array_map(static fn (Partition $p) => [$p->name => $p->value], $partitions->toArray())))) + map_entry('partitions', \array_merge(...\array_values(\array_map(static fn (Partition $p) => [$p->name => $p->value], $partitions->toArray()))), type_map(type_string(), type_string())) ); $signal = yield rows($row); diff --git a/src/core/etl/src/Flow/ETL/Function/Collect.php b/src/core/etl/src/Flow/ETL/Function/Collect.php index 634e11ede..5f03ba7b2 100644 --- a/src/core/etl/src/Flow/ETL/Function/Collect.php +++ b/src/core/etl/src/Flow/ETL/Function/Collect.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Function; -use function Flow\ETL\DSL\array_entry; +use function Flow\ETL\DSL\to_entry; use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row; use Flow\ETL\Row\{Entry, Reference}; @@ -41,6 +41,6 @@ public function result() : Entry $this->ref->as($this->ref->name() . '_collection'); } - return array_entry($this->ref->name(), $this->collection); + return to_entry($this->ref->name(), $this->collection); } } diff --git a/src/core/etl/src/Flow/ETL/Function/CollectUnique.php b/src/core/etl/src/Flow/ETL/Function/CollectUnique.php index b6bd5390d..75ac4e172 100644 --- a/src/core/etl/src/Flow/ETL/Function/CollectUnique.php +++ b/src/core/etl/src/Flow/ETL/Function/CollectUnique.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Function; -use function Flow\ETL\DSL\array_entry; +use function Flow\ETL\DSL\{to_entry}; use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row; use Flow\ETL\Row\{Entry, Reference}; @@ -46,6 +46,6 @@ public function result() : Entry $this->ref->as($this->ref->name() . '_collection_unique'); } - return array_entry($this->ref->name(), $this->collection); + return to_entry($this->ref->name(), $this->collection); } } diff --git a/src/core/etl/src/Flow/ETL/Function/JsonDecode.php b/src/core/etl/src/Flow/ETL/Function/JsonDecode.php index 3cfcbb1a6..23d26d003 100644 --- a/src/core/etl/src/Flow/ETL/Function/JsonDecode.php +++ b/src/core/etl/src/Flow/ETL/Function/JsonDecode.php @@ -4,6 +4,7 @@ namespace Flow\ETL\Function; +use function Flow\ETL\DSL\{type_array, type_string}; use Flow\ETL\Row; final class JsonDecode extends ScalarFunctionChain @@ -16,13 +17,17 @@ public function __construct( public function eval(Row $row) : mixed { - $value = (new Parameter($this->value))->asString($row); + $value = (new Parameter($this->value))->as($row, type_string(), type_array()); $flags = (int) (new Parameter($this->flags))->asInt($row); if ($value === null) { return null; } + if (\is_array($value)) { + return $value; + } + try { return \json_decode($value, true, 512, $flags); } catch (\JsonException $e) { diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/Caster.php b/src/core/etl/src/Flow/ETL/PHP/Type/Caster.php index 8ea0e4962..7b6874b18 100644 --- a/src/core/etl/src/Flow/ETL/PHP/Type/Caster.php +++ b/src/core/etl/src/Flow/ETL/PHP/Type/Caster.php @@ -6,17 +6,36 @@ use function Flow\ETL\DSL\{type_array, type_boolean, + type_date, type_datetime, type_float, type_integer, type_json, type_null, type_string, + type_time, type_uuid, - type_xml -}; + type_xml}; use Flow\ETL\Exception\RuntimeException; -use Flow\ETL\PHP\Type\Caster\{ArrayCastingHandler, BooleanCastingHandler, CastingContext, CastingHandler, DateTimeCastingHandler, EnumCastingHandler, FloatCastingHandler, IntegerCastingHandler, JsonCastingHandler, ListCastingHandler, MapCastingHandler, NullCastingHandler, ObjectCastingHandler, StringCastingHandler, StructureCastingHandler, UuidCastingHandler, XMLCastingHandler}; +use Flow\ETL\PHP\Type\Caster\{ArrayCastingHandler, + BooleanCastingHandler, + CastingContext, + CastingHandler, + DateCastingHandler, + DateTimeCastingHandler, + EnumCastingHandler, + FloatCastingHandler, + IntegerCastingHandler, + JsonCastingHandler, + ListCastingHandler, + MapCastingHandler, + NullCastingHandler, + ObjectCastingHandler, + StringCastingHandler, + StructureCastingHandler, + TimeCastingHandler, + UuidCastingHandler, + XMLCastingHandler}; final class Caster { @@ -38,6 +57,8 @@ public static function default() : self type_uuid()->toString() => new UuidCastingHandler(), 'object' => new ObjectCastingHandler(), type_datetime()->toString() => new DateTimeCastingHandler(), + type_date()->toString() => new DateCastingHandler(), + type_time()->toString() => new TimeCastingHandler(), type_json()->toString() => new JsonCastingHandler(), type_array()->toString() => new ArrayCastingHandler(), 'list' => new ListCastingHandler(), diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/Caster/DateCastingHandler.php b/src/core/etl/src/Flow/ETL/PHP/Type/Caster/DateCastingHandler.php new file mode 100644 index 000000000..f8aa60930 --- /dev/null +++ b/src/core/etl/src/Flow/ETL/PHP/Type/Caster/DateCastingHandler.php @@ -0,0 +1,56 @@ +setTime(0, 0, 0, 0); + } + + if ($value instanceof \DOMElement) { + $value = $value->nodeValue; + } + + if ($value instanceof \DateTime) { + return \DateTimeImmutable::createFromMutable($value)->setTime(0, 0, 0, 0); + } + + try { + if (\is_string($value)) { + return (new \DateTimeImmutable($value))->setTime(0, 0, 0, 0); + } + + if (\is_numeric($value)) { + return (new \DateTimeImmutable('@' . $value))->setTime(0, 0, 0, 0); + } + + if (\is_bool($value)) { + /* @phpstan-ignore-next-line */ + return (new \DateTimeImmutable('@' . $value))->setTime(0, 0, 0, 0); + } + + if ($value instanceof \DateInterval) { + return (new \DateTimeImmutable('@0'))->add($value)->setTime(0, 0, 0, 0); + + } + } catch (\Throwable $e) { + throw new CastingException($value, type_date()); + } + + throw new CastingException($value, $type); + } +} diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/Caster/TimeCastingHandler.php b/src/core/etl/src/Flow/ETL/PHP/Type/Caster/TimeCastingHandler.php new file mode 100644 index 000000000..abf57e629 --- /dev/null +++ b/src/core/etl/src/Flow/ETL/PHP/Type/Caster/TimeCastingHandler.php @@ -0,0 +1,42 @@ +diff(new \DateTimeImmutable($value->format('Y-m-d')), true); + } + + if ($value instanceof \DateInterval) { + return $value; + } + + if ($value instanceof \DOMElement) { + $value = $value->nodeValue; + } + + try { + if (\is_string($value)) { + return new \DateInterval($value); + } + } catch (\Throwable $e) { + throw new CastingException($value, type_time()); + } + + throw new CastingException($value, $type); + } +} diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/Logical/DateType.php b/src/core/etl/src/Flow/ETL/PHP/Type/Logical/DateType.php new file mode 100644 index 000000000..65f84cfda --- /dev/null +++ b/src/core/etl/src/Flow/ETL/PHP/Type/Logical/DateType.php @@ -0,0 +1,84 @@ +nullable && $value === null) { + return true; + } + + return $value instanceof \DateTimeInterface && $value->format('H:i:s') === '00:00:00'; + } + + public function makeNullable(bool $nullable) : self + { + return new self($nullable); + } + + public function merge(Type $type) : self + { + if ($type instanceof NullType) { + return $this->makeNullable(true); + } + + if (!$type instanceof self) { + throw new InvalidArgumentException('Cannot merge different types, ' . $this->toString() . ' and ' . $type->toString()); + } + + return new self($this->nullable || $type->nullable()); + } + + public function normalize() : array + { + return [ + 'type' => 'date', + 'nullable' => $this->nullable, + ]; + } + + public function nullable() : bool + { + return $this->nullable; + } + + public function toString() : string + { + return ($this->nullable ? '?' : '') . 'date'; + } +} diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/Logical/TimeType.php b/src/core/etl/src/Flow/ETL/PHP/Type/Logical/TimeType.php new file mode 100644 index 000000000..9b8c12946 --- /dev/null +++ b/src/core/etl/src/Flow/ETL/PHP/Type/Logical/TimeType.php @@ -0,0 +1,84 @@ +nullable && $value === null) { + return true; + } + + return $value instanceof \DateInterval; + } + + public function makeNullable(bool $nullable) : self + { + return new self($nullable); + } + + public function merge(Type $type) : self + { + if ($type instanceof NullType) { + return $this->makeNullable(true); + } + + if (!$type instanceof self) { + throw new InvalidArgumentException('Cannot merge different types, ' . $this->toString() . ' and ' . $type->toString()); + } + + return new self($this->nullable || $type->nullable()); + } + + public function normalize() : array + { + return [ + 'type' => 'time', + 'nullable' => $this->nullable, + ]; + } + + public function nullable() : bool + { + return $this->nullable; + } + + public function toString() : string + { + return ($this->nullable ? '?' : '') . 'time'; + } +} diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/TypeDetector.php b/src/core/etl/src/Flow/ETL/PHP/Type/TypeDetector.php index 85f0c5ff3..4147d6f86 100644 --- a/src/core/etl/src/Flow/ETL/PHP/Type/TypeDetector.php +++ b/src/core/etl/src/Flow/ETL/PHP/Type/TypeDetector.php @@ -6,6 +6,7 @@ use function Flow\ETL\DSL\{type_array, type_boolean, + type_date, type_datetime, type_float, type_int, @@ -14,6 +15,7 @@ type_null, type_object, type_string, + type_time, type_uuid, type_xml, type_xml_element}; @@ -97,6 +99,14 @@ public function detectType(mixed $value) : Type return type_uuid(); } + if (type_time()->isValid($value)) { + return type_time(); + } + + if (type_date()->isValid($value)) { + return type_date(); + } + if (type_datetime()->isValid($value)) { return type_datetime(); } diff --git a/src/core/etl/src/Flow/ETL/PHP/Type/TypeFactory.php b/src/core/etl/src/Flow/ETL/PHP/Type/TypeFactory.php index d3c83f8d4..fa6c51ca3 100644 --- a/src/core/etl/src/Flow/ETL/PHP/Type/TypeFactory.php +++ b/src/core/etl/src/Flow/ETL/PHP/Type/TypeFactory.php @@ -6,10 +6,12 @@ use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\PHP\Type\Logical\{DateTimeType, + DateType, JsonType, ListType, MapType, StructureType, + TimeType, UuidType, XMLElementType, XMLType}; @@ -31,6 +33,8 @@ public static function fromArray(array $data) : Type 'null' => NullType::fromArray($data), 'object' => ObjectType::fromArray($data), 'resource' => ResourceType::fromArray($data), + 'time' => TimeType::fromArray($data), + 'date' => DateType::fromArray($data), 'datetime' => DateTimeType::fromArray($data), 'json' => JsonType::fromArray($data), 'uuid' => UuidType::fromArray($data), diff --git a/src/core/etl/src/Flow/ETL/Row/Comparator/WeakObjectComparator.php b/src/core/etl/src/Flow/ETL/Row/Comparator/WeakObjectComparator.php deleted file mode 100644 index e6b8e5618..000000000 --- a/src/core/etl/src/Flow/ETL/Row/Comparator/WeakObjectComparator.php +++ /dev/null @@ -1,33 +0,0 @@ -entries()->all() as $entry) { - if ($entry instanceof Row\Entry\ObjectEntry) { - if ($entry->value() != $nextRow->get($entry->name())->value()) { - return false; - } - } else { - if (!$entry->isEqual($nextRow->get($entry->name()))) { - return false; - } - } - } - - return true; - } catch (InvalidArgumentException) { - return false; - } - } -} diff --git a/src/core/etl/src/Flow/ETL/Row/Entry/ArrayEntry.php b/src/core/etl/src/Flow/ETL/Row/Entry/DateEntry.php similarity index 52% rename from src/core/etl/src/Flow/ETL/Row/Entry/ArrayEntry.php rename to src/core/etl/src/Flow/ETL/Row/Entry/DateEntry.php index aa57920a7..66e2bdfcf 100644 --- a/src/core/etl/src/Flow/ETL/Row/Entry/ArrayEntry.php +++ b/src/core/etl/src/Flow/ETL/Row/Entry/DateEntry.php @@ -4,37 +4,48 @@ namespace Flow\ETL\Row\Entry; -use function Flow\ETL\DSL\type_array; -use Flow\ArrayComparison\ArrayComparison; +use function Flow\ETL\DSL\type_date; use Flow\ETL\Exception\InvalidArgumentException; -use Flow\ETL\PHP\Type\Native\ArrayType; +use Flow\ETL\PHP\Type\Logical\DateType; use Flow\ETL\PHP\Type\Type; use Flow\ETL\Row\Schema\Definition; use Flow\ETL\Row\{Entry, Reference}; /** - * @implements Entry + * @implements Entry */ -final class ArrayEntry implements Entry +final class DateEntry implements Entry { use EntryRef; - private readonly ArrayType $type; + private readonly DateType $type; + + private readonly ?\DateTimeInterface $value; /** - * @param array $value - * * @throws InvalidArgumentException */ - public function __construct( - private readonly string $name, - private readonly ?array $value, - ) { - if ('' === $name) { + public function __construct(private readonly string $name, \DateTimeInterface|string|null $value) + { + if ($name === '') { throw InvalidArgumentException::because('Entry name cannot be empty'); } - $this->type = type_array([] === $this->value, $this->value === null); + if (\is_string($value)) { + try { + $this->value = (new \DateTimeImmutable($value))->setTime(0, 0, 0, 0); + } catch (\Exception $e) { + throw new InvalidArgumentException("Invalid value given: '{$value}', reason: " . $e->getMessage(), previous: $e); + } + } elseif ($value instanceof \DateTime) { + $this->value = (\DateTimeImmutable::createFromMutable($value))->setTime(0, 0, 0, 0); + } elseif ($value instanceof \DateTimeImmutable) { + $this->value = $value->setTime(0, 0, 0, 0); + } else { + $this->value = $value; + } + + $this->type = type_date($this->value === null); } public function __toString() : string @@ -44,7 +55,7 @@ public function __toString() : string public function definition() : Definition { - return Definition::array($this->name, $this->type->nullable()); + return Definition::dateTime($this->name, $this->type->nullable()); } public function is(string|Reference $name) : bool @@ -58,24 +69,7 @@ public function is(string|Reference $name) : bool public function isEqual(Entry $entry) : bool { - $entryValue = $entry->value(); - $thisValue = $this->value(); - - if ($entryValue === null && $thisValue !== null) { - return false; - } - - if ($entryValue !== null && $thisValue === null) { - return false; - } - - if ($entryValue === null && $thisValue === null) { - return $this->is($entry->name()) - && $entry instanceof self - && $this->type->isEqual($entry->type); - } - - return $this->is($entry->name()) && $entry instanceof self && $this->type->isEqual($entry->type) && (new ArrayComparison())->equals($thisValue, $entryValue); + return $this->is($entry->name()) && $entry instanceof self && $this->type->isEqual($entry->type) && $this->value() == $entry->value(); } public function map(callable $mapper) : Entry @@ -95,11 +89,13 @@ public function rename(string $name) : Entry public function toString() : string { - if ($this->value === null) { + $value = $this->value; + + if ($value === null) { return ''; } - return \json_encode($this->value, \JSON_THROW_ON_ERROR); + return $value->format('Y-m-d'); } public function type() : Type @@ -107,7 +103,7 @@ public function type() : Type return $this->type; } - public function value() : ?array + public function value() : ?\DateTimeInterface { return $this->value; } diff --git a/src/core/etl/src/Flow/ETL/Row/Entry/JsonEntry.php b/src/core/etl/src/Flow/ETL/Row/Entry/JsonEntry.php index 3361d318b..6aa339db6 100644 --- a/src/core/etl/src/Flow/ETL/Row/Entry/JsonEntry.php +++ b/src/core/etl/src/Flow/ETL/Row/Entry/JsonEntry.php @@ -13,7 +13,7 @@ use Flow\ETL\Row\{Entry, Reference}; /** - * @implements Entry + * @implements Entry> */ final class JsonEntry implements Entry { @@ -89,8 +89,8 @@ public function is(string|Reference $name) : bool public function isEqual(Entry $entry) : bool { - $entryValue = $entry->value(); - $thisValue = $this->value(); + $entryValue = $entry instanceof self ? $entry->value : $entry->value(); + $thisValue = $this->value; if ($entryValue === null && $thisValue !== null) { return false; @@ -106,10 +106,6 @@ public function isEqual(Entry $entry) : bool && $this->type->isEqual($entry->type); } - /** @phpstan-ignore-next-line */ - $thisValue = \json_decode($thisValue, true, flags: \JSON_THROW_ON_ERROR); - $entryValue = \json_decode($entryValue, true, flags: \JSON_THROW_ON_ERROR); - return $this->is($entry->name()) && $entry instanceof self && $this->type->isEqual($entry->type) && (new ArrayComparison())->equals($thisValue, $entryValue); } @@ -133,13 +129,15 @@ public function rename(string $name) : Entry public function toString() : string { - $value = $this->value(); - - if ($value === null) { + if ($this->value === null) { return ''; } - return $value; + if (!\count($this->value) && $this->object) { + return '{}'; + } + + return \json_encode($this->value, JSON_THROW_ON_ERROR); } public function type() : Type @@ -148,19 +146,11 @@ public function type() : Type } /** - * @throws \JsonException + * @return null|array */ - public function value() : ?string + public function value() : ?array { - if ($this->value === null) { - return null; - } - - if (!\count($this->value) && $this->object) { - return '{}'; - } - - return \json_encode($this->value, JSON_THROW_ON_ERROR); + return $this->value; } public function withValue(mixed $value) : Entry diff --git a/src/core/etl/src/Flow/ETL/Row/Entry/ObjectEntry.php b/src/core/etl/src/Flow/ETL/Row/Entry/ObjectEntry.php deleted file mode 100644 index 5b51b17f1..000000000 --- a/src/core/etl/src/Flow/ETL/Row/Entry/ObjectEntry.php +++ /dev/null @@ -1,103 +0,0 @@ - - */ -final class ObjectEntry implements Entry -{ - use EntryRef; - - private readonly ObjectType $type; - - /** - * @throws InvalidArgumentException - */ - public function __construct(private readonly string $name, private readonly ?object $value) - { - if ('' === $name) { - throw InvalidArgumentException::because('Entry name cannot be empty'); - } - - $this->type = type_object($value === null ? \stdClass::class : $value::class, $value === null); - } - - public function __toString() : string - { - return $this->toString(); - } - - public function definition() : Definition - { - return Definition::object($this->name, $this->type); - } - - public function is(string|Reference $name) : bool - { - if ($name instanceof Reference) { - return $this->name === $name->name(); - } - - return $this->name === $name; - } - - public function isEqual(Entry $entry) : bool - { - return $this->is($entry->name()) - && $entry instanceof self - && $this->type->isEqual($entry->type) - && \serialize($this->value) === \serialize($entry->value); - } - - public function map(callable $mapper) : Entry - { - return new self($this->name, $mapper($this->value())); - } - - public function name() : string - { - return $this->name; - } - - /** - * @throws InvalidArgumentException - */ - public function rename(string $name) : Entry - { - return new self($name, $this->value); - } - - public function toString() : string - { - if ($this->value === null) { - return ''; - } - - return ($this->type->nullable() ? '?' : '') . \preg_replace('!\s+!', ' ', \str_replace("\n", '', \print_r($this->value(), true))); - } - - public function type() : Type - { - return $this->type; - } - - public function value() : ?object - { - return $this->value; - } - - public function withValue(mixed $value) : Entry - { - return new self($this->name, $value); - } -} diff --git a/src/core/etl/src/Flow/ETL/Row/Entry/TimeEntry.php b/src/core/etl/src/Flow/ETL/Row/Entry/TimeEntry.php new file mode 100644 index 000000000..4f9c1b631 --- /dev/null +++ b/src/core/etl/src/Flow/ETL/Row/Entry/TimeEntry.php @@ -0,0 +1,223 @@ + + */ +final class TimeEntry implements Entry +{ + use EntryRef; + + private readonly TimeType $type; + + /** + * Time represented php \DateInterval. + * + * @var null|\DateInterval + */ + private readonly ?\DateInterval $value; + + /** + * @psalm-suppress InaccessibleProperty + * + * @throws InvalidArgumentException + */ + public function __construct(private readonly string $name, \DateInterval|string|null $value) + { + if ($name === '') { + throw InvalidArgumentException::because('Entry name cannot be empty'); + } + + if ($value instanceof \DateInterval) { + if ($value->y !== 0 || $value->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to TimeEntry. Given" . \json_encode($value, JSON_THROW_ON_ERROR)); + } + + $this->value = $value; + } elseif (\is_string($value)) { + try { + $interval = new \DateInterval($value); + + if ($interval->y !== 0 || $interval->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to microseconds. Given" . \json_encode($interval, JSON_THROW_ON_ERROR)); + } + + $this->value = $interval; + } catch (\Throwable $dateIntervalException) { + try { + $dateTime = new \DateTimeImmutable($value); + + // Get hours, minutes, seconds, and fractional seconds + $hours = (int) $dateTime->format('H'); + $minutes = (int) $dateTime->format('i'); + $seconds = (int) $dateTime->format('s'); + $fraction = (float) ('0.' . $dateTime->format('u')); // Microseconds as fractional part + + // Construct the DateInterval + $interval = new \DateInterval('PT' . $hours . 'H' . $minutes . 'M' . $seconds . 'S'); + $interval->f = $fraction; // Set the fractional seconds + + if ($interval->y !== 0 || $interval->m !== 0) { + throw new InvalidArgumentException("Relative DateInterval (with months/years) can't be converted to microseconds. Given" . \json_encode($interval, JSON_THROW_ON_ERROR)); + } + + $this->value = $interval; + } catch (\Throwable $dateTimeException) { + throw $dateIntervalException; + } + } + } else { + $this->value = null; + } + + $this->type = type_time($this->value === null); + } + + public static function fromDays(string $name, int $days) : self + { + return new self($name, 'P' . $days . 'D'); + } + + public static function fromHours(string $name, int $hours) : self + { + return new self($name, 'PT' . $hours . 'H'); + } + + /** + * @psalm-suppress InaccessibleProperty + */ + public static function fromMicroseconds(string $name, int $microseconds) : self + { + $seconds = intdiv($microseconds, 1_000_000); + $fraction = ($microseconds % 1_000_000) / 1_000_000; + + $interval = new \DateInterval('PT' . $seconds . 'S'); + $interval->f = $fraction; + + return new self($name, $interval); + } + + /** + * @psalm-suppress InaccessibleProperty + */ + public static function fromMilliseconds(string $name, int $milliseconds) : self + { + $seconds = intdiv($milliseconds, 1000); + $fraction = ($milliseconds % 1000) / 1000; + + $interval = new \DateInterval('PT' . $seconds . 'S'); + $interval->f = $fraction; + + return new self($name, $interval); + } + + public static function fromMinutes(string $name, int $minutes) : self + { + return new self($name, 'PT' . $minutes . 'M'); + } + + public static function fromSeconds(string $name, int $seconds) : self + { + return new self($name, 'PT' . $seconds . 'S'); + } + + public static function fromString(string $name, string $time) : self + { + return new self($name, $time); + } + + public function __toString() : string + { + return $this->toString(); + } + + public function definition() : Definition + { + return Definition::dateTime($this->name, $this->type->nullable()); + } + + public function is(string|Reference $name) : bool + { + if ($name instanceof Reference) { + return $this->name === $name->name(); + } + + return $this->name === $name; + } + + public function isEqual(Entry $entry) : bool + { + $entryValue = $entry->value(); + $thisValue = $this->value(); + + if ($entryValue === null && $thisValue === null) { + return true; + } + + if ($entryValue === null || $thisValue === null) { + return false; + } + + return $this->is($entry->name()) + && $entry instanceof self + && $this->type->isEqual($entry->type) + && date_interval_to_microseconds($thisValue) == date_interval_to_microseconds($entryValue); + } + + public function map(callable $mapper) : Entry + { + return new self($this->name, $mapper($this->value)); + } + + public function name() : string + { + return $this->name; + } + + public function rename(string $name) : Entry + { + return new self($name, $this->value); + } + + public function toString() : string + { + $value = $this->value; + + if ($value === null) { + return ''; + } + + $totalHours = ($value->d * 24) + $value->h; // Convert days to hours and add to hours + + if ($value->f && $value->f > 0) { + return sprintf('%02d:%02d:%02d.%06d', $totalHours, $value->i, $value->s, $value->f * 1e6); + } + + return sprintf('%02d:%02d:%02d', $totalHours, $value->i, $value->s); + } + + public function type() : Type + { + return $this->type; + } + + public function value() : ?\DateInterval + { + return $this->value; + } + + public function withValue(mixed $value) : Entry + { + return new self($this->name, $value); + } +} diff --git a/src/core/etl/src/Flow/ETL/Row/Factory/NativeEntryFactory.php b/src/core/etl/src/Flow/ETL/Row/Factory/NativeEntryFactory.php index f712bbd5d..fd6a1945b 100644 --- a/src/core/etl/src/Flow/ETL/Row/Factory/NativeEntryFactory.php +++ b/src/core/etl/src/Flow/ETL/Row/Factory/NativeEntryFactory.php @@ -4,8 +4,8 @@ namespace Flow\ETL\Row\Factory; -use function Flow\ETL\DSL\{array_entry, - bool_entry, +use function Flow\ETL\DSL\{bool_entry, + date_entry, datetime_entry, enum_entry, float_entry, @@ -14,10 +14,9 @@ enum_entry, json_entry, json_object_entry, map_entry, - obj_entry, - object_entry, str_entry, struct_entry, + time_entry, type_boolean, type_float, type_int, @@ -28,10 +27,12 @@ enum_entry, use Flow\ETL\Exception\{InvalidArgumentException, RuntimeException, SchemaDefinitionNotFoundException}; use Flow\ETL\PHP\Type\Caster\StringCastingHandler\StringTypeChecker; use Flow\ETL\PHP\Type\Logical\{DateTimeType, + DateType, JsonType, ListType, MapType, StructureType, + TimeType, UuidType, XMLElementType, XMLType}; @@ -103,7 +104,7 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $ } } - if ($valueType instanceof JsonType) { + if ($valueType instanceof JsonType || $valueType instanceof ArrayType) { return json_entry($entryName, $value); } @@ -115,6 +116,14 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $ return uuid_entry($entryName, (string) $value); } + if ($valueType instanceof TimeType) { + return time_entry($entryName, $value); + } + + if ($valueType instanceof DateType) { + return date_entry($entryName, $value); + } + if ($valueType instanceof DateTimeType) { return datetime_entry($entryName, $value); } @@ -136,7 +145,15 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $ return xml_element_entry($entryName, $value); } + if ($valueType->class === \DateInterval::class) { + return time_entry($entryName, $value); + } + if (\in_array($valueType->class, [\DateTimeImmutable::class, \DateTimeInterface::class, \DateTime::class], true)) { + if ($value->format('H:i:s') === '00:00:00') { + return date_entry($entryName, $value); + } + return datetime_entry($entryName, $value); } @@ -148,17 +165,13 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $ return uuid_entry($entryName, $value); } - return object_entry($entryName, $value); + throw new InvalidArgumentException("{$entryName}: {$valueType->toString()} can't be converted to any known Entry, please normalize that object first."); } if ($valueType instanceof EnumType) { return enum_entry($entryName, $value); } - if ($valueType instanceof ArrayType) { - return array_entry($entryName, $value); - } - if ($valueType instanceof ListType) { return new Entry\ListEntry($entryName, $value, $valueType); } @@ -187,15 +200,15 @@ private function fromDefinition(Definition $definition, mixed $value) : Entry ScalarType::BOOLEAN => bool_entry($definition->entry()->name(), null), default => throw new InvalidArgumentException("Can't convert value into entry \"{$definition->entry()}\""), }, - ObjectType::class => obj_entry($definition->entry()->name(), null), - ArrayType::class => array_entry($definition->entry()->name(), null), MapType::class => map_entry($definition->entry()->name(), null, $type), StructureType::class => struct_entry($definition->entry()->name(), null, $type), ListType::class => new Entry\ListEntry($definition->entry()->name(), null, $type), UuidType::class => uuid_entry($definition->entry()->name(), null), DateTimeType::class => datetime_entry($definition->entry()->name(), null), + TimeType::class => time_entry($definition->entry()->name(), null), + DateType::class => date_entry($definition->entry()->name(), null), EnumType::class => enum_entry($definition->entry()->name(), null), - JsonType::class => json_entry($definition->entry()->name(), null), + ArrayType::class, JsonType::class => json_entry($definition->entry()->name(), null), default => throw new InvalidArgumentException("Can't convert value into entry \"{$definition->entry()}\""), }; } @@ -219,6 +232,14 @@ private function fromDefinition(Definition $definition, mixed $value) : Entry return uuid_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); } + if ($type instanceof DateType) { + return date_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); + } + + if ($type instanceof TimeType) { + return time_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); + } + if ($type instanceof DateTimeType) { return datetime_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); } @@ -235,12 +256,12 @@ private function fromDefinition(Definition $definition, mixed $value) : Entry } } - if ($type instanceof ObjectType) { - return obj_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); + if ($type instanceof ArrayType) { + return json_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); } - if ($type instanceof ArrayType) { - return array_entry($definition->entry()->name(), is_type([$type], $value) ? $value : $this->caster->to($type)->value($value)); + if ($type instanceof ObjectType) { + throw new InvalidArgumentException("{$definition->entry()->name()}: {$type->toString()} can't be converted to any known Entry, please normalize that object first."); } if ($type instanceof MapType) { diff --git a/src/core/etl/src/Flow/ETL/Row/Schema/Definition.php b/src/core/etl/src/Flow/ETL/Row/Schema/Definition.php index b8298f668..1135ea69c 100644 --- a/src/core/etl/src/Flow/ETL/Row/Schema/Definition.php +++ b/src/core/etl/src/Flow/ETL/Row/Schema/Definition.php @@ -4,8 +4,8 @@ namespace Flow\ETL\Row\Schema; -use function Flow\ETL\DSL\{type_array, - type_boolean, +use function Flow\ETL\DSL\{type_boolean, + type_date, type_datetime, type_enum, type_float, @@ -13,14 +13,27 @@ type_json, type_list, type_string, + type_time, type_uuid, type_xml, type_xml_element}; use Flow\ETL\Exception\{InvalidArgumentException, RuntimeException}; use Flow\ETL\PHP\Type\Logical\{ListType, MapType, StructureType}; -use Flow\ETL\PHP\Type\Native\ObjectType; use Flow\ETL\PHP\Type\{Type, TypeFactory}; -use Flow\ETL\Row\Entry\{ArrayEntry, BooleanEntry, DateTimeEntry, EnumEntry, FloatEntry, IntegerEntry, JsonEntry, ListEntry, MapEntry, ObjectEntry, StringEntry, StructureEntry, UuidEntry, XMLEntry}; +use Flow\ETL\Row\Entry\{BooleanEntry, + DateEntry, + DateTimeEntry, + EnumEntry, + FloatEntry, + IntegerEntry, + JsonEntry, + ListEntry, + MapEntry, + StringEntry, + StructureEntry, + TimeEntry, + UuidEntry, + XMLEntry}; use Flow\ETL\Row\{Entry, EntryReference, Reference}; final class Definition @@ -46,14 +59,14 @@ public function __construct( $this->ref = EntryReference::init($ref); } - public static function array(string|Reference $entry, bool $empty = false, bool $nullable = false, ?Metadata $metadata = null) : self + public static function boolean(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self { - return new self($entry, ArrayEntry::class, type_array($empty, $nullable), $metadata); + return new self($entry, BooleanEntry::class, type_boolean($nullable), $metadata); } - public static function boolean(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self + public static function date(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self { - return new self($entry, BooleanEntry::class, type_boolean($nullable), $metadata); + return new self($entry, DateEntry::class, type_date($nullable), $metadata); } public static function dateTime(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self @@ -100,7 +113,6 @@ public static function fromArray(array $definition) : self return new self( $definition['ref'], match ($definition['type']['type']) { - 'array' => ArrayEntry::class, 'scalar' => match ($definition['type']['scalar_type']) { 'boolean' => BooleanEntry::class, 'float' => FloatEntry::class, @@ -109,11 +121,12 @@ public static function fromArray(array $definition) : self default => throw new InvalidArgumentException(\sprintf('Unknown scalar type "%s"', \json_encode($definition['type']['scalar_type']))), }, 'datetime' => DateTimeEntry::class, + 'time' => TimeEntry::class, + 'date' => DateEntry::class, 'enum' => EnumEntry::class, - 'json' => JsonEntry::class, + 'json', 'array' => JsonEntry::class, 'list' => ListEntry::class, 'map' => MapEntry::class, - 'object' => ObjectEntry::class, 'structure' => StructureEntry::class, 'uuid' => UuidEntry::class, 'xml' => XMLEntry::class, @@ -155,16 +168,6 @@ public static function map(string|Reference $entry, MapType $type, ?Metadata $me ); } - public static function object(string|Reference $entry, ObjectType $type, ?Metadata $metadata = null) : self - { - return new self( - $entry, - ObjectEntry::class, - $type, - $metadata - ); - } - public static function string(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self { return new self($entry, StringEntry::class, type_string($nullable), $metadata); @@ -180,6 +183,11 @@ public static function structure(string|Reference $entry, StructureType $type, ? ); } + public static function time(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self + { + return new self($entry, TimeEntry::class, type_time($nullable), $metadata); + } + public static function uuid(string|Reference $entry, bool $nullable = false, ?Metadata $metadata = null) : self { return new self($entry, UuidEntry::class, type_uuid($nullable), $metadata); @@ -260,8 +268,8 @@ public function merge(self $definition) : self if (!$this->type->isEqual($definition->type)) { return new self( $this->ref, - ArrayEntry::class, - type_array(false, $this->isNullable() || $definition->isNullable()), + JsonEntry::class, + type_json($this->isNullable() || $definition->isNullable()), $this->metadata->merge($definition->metadata) ); } @@ -287,20 +295,38 @@ public function merge(self $definition) : self ); } - if (\in_array(IntegerEntry::class, $entryClasses, true) && \in_array(FloatEntry::class, $entryClasses, true)) { + if (\in_array(TimeEntry::class, $entryClasses, true) && \in_array(DateEntry::class, $entryClasses, true)) { return new self( $this->ref, - FloatEntry::class, - type_float($this->isNullable() || $definition->isNullable()), + DateTimeEntry::class, + type_datetime($this->isNullable() || $definition->isNullable()), + $this->metadata->merge($definition->metadata) + ); + } + + if (\in_array(TimeEntry::class, $entryClasses, true) && \in_array(DateTimeEntry::class, $entryClasses, true)) { + return new self( + $this->ref, + DateTimeEntry::class, + type_datetime($this->isNullable() || $definition->isNullable()), + $this->metadata->merge($definition->metadata) + ); + } + + if (\in_array(DateEntry::class, $entryClasses, true) && \in_array(DateTimeEntry::class, $entryClasses, true)) { + return new self( + $this->ref, + DateTimeEntry::class, + type_datetime($this->isNullable() || $definition->isNullable()), $this->metadata->merge($definition->metadata) ); } - if (\in_array(ArrayEntry::class, $entryClasses, true)) { + if (\in_array(IntegerEntry::class, $entryClasses, true) && \in_array(FloatEntry::class, $entryClasses, true)) { return new self( $this->ref, - ArrayEntry::class, - type_array(false, $this->isNullable() || $definition->isNullable()), + FloatEntry::class, + type_float($this->isNullable() || $definition->isNullable()), $this->metadata->merge($definition->metadata) ); } diff --git a/src/core/etl/src/Flow/ETL/Transformer/GroupToArrayTransformer.php b/src/core/etl/src/Flow/ETL/Transformer/GroupToArrayTransformer.php index a495508e0..5a1b09e92 100644 --- a/src/core/etl/src/Flow/ETL/Transformer/GroupToArrayTransformer.php +++ b/src/core/etl/src/Flow/ETL/Transformer/GroupToArrayTransformer.php @@ -33,7 +33,7 @@ public function transform(Rows $rows, FlowContext $context) : Rows foreach ($entries as $entry) { $rows = $rows->add( Row::create( - new Entry\ArrayEntry( + new Entry\JsonEntry( $this->newEntryName, $entry ) diff --git a/src/core/etl/src/Flow/ETL/Transformer/OrderEntries/TypePriorities.php b/src/core/etl/src/Flow/ETL/Transformer/OrderEntries/TypePriorities.php index 9f834dc38..e23d44b67 100644 --- a/src/core/etl/src/Flow/ETL/Transformer/OrderEntries/TypePriorities.php +++ b/src/core/etl/src/Flow/ETL/Transformer/OrderEntries/TypePriorities.php @@ -20,14 +20,12 @@ final class TypePriorities Entry\DateTimeEntry::class => 5, Entry\StringEntry::class => 6, Entry\EnumEntry::class => 7, - Entry\ArrayEntry::class => 8, - Entry\ListEntry::class => 9, - Entry\JsonEntry::class => 10, - Entry\MapEntry::class => 11, - Entry\ObjectEntry::class => 12, - Entry\StructureEntry::class => 13, - Entry\XMLEntry::class => 14, - Entry\XMLElementEntry::class => 15, + Entry\ListEntry::class => 8, + Entry\JsonEntry::class => 9, + Entry\MapEntry::class => 10, + Entry\StructureEntry::class => 11, + Entry\XMLEntry::class => 12, + Entry\XMLElementEntry::class => 13, ]; /** diff --git a/src/core/etl/tests/Flow/ETL/Tests/Double/FakeExtractor.php b/src/core/etl/tests/Flow/ETL/Tests/Double/FakeExtractor.php index 004390fab..97ad0a712 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Double/FakeExtractor.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Double/FakeExtractor.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Double; -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ bool_entry, datetime_entry, enum_entry, @@ -14,7 +14,6 @@ enum_entry, json_entry, list_entry, map_entry, - object_entry, row, rows, str_entry, @@ -57,13 +56,6 @@ public function extract(FlowContext $context) : \Generator str_entry('null', null), uuid_entry('uuid', new \Flow\ETL\PHP\Value\Uuid(Uuid::uuid4())), json_entry('json', ['id' => $id, 'status' => 'NEW']), - array_entry( - 'array', - [ - ['id' => 1, 'status' => 'NEW'], - ['id' => 2, 'status' => 'PENDING'], - ] - ), list_entry('list', [1, 2, 3], type_list(type_int())), list_entry('list_of_datetimes', [new \DateTimeImmutable(), new \DateTimeImmutable(), new \DateTimeImmutable()], type_list(type_datetime())), map_entry( @@ -94,7 +86,6 @@ public function extract(FlowContext $context) : \Generator ), ]), ), - object_entry('object', new \ArrayIterator([1, 2, 3])), enum_entry('enum', BackedStringEnum::three) ) ); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/DisplayTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/DisplayTest.php index a0d4085e0..1167d7f46 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/DisplayTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/DisplayTest.php @@ -4,7 +4,31 @@ namespace Flow\ETL\Tests\Integration\DataFrame; -use function Flow\ETL\DSL\{array_entry, bool_entry, datetime_entry, df, enum_entry, float_entry, from_array, from_rows, int_entry, list_entry, map_entry, object_entry, ref, row, rows, str_entry, string_entry, struct_element, struct_entry, struct_type, type_int, type_list, type_map, type_string, xml_entry}; +use function Flow\ETL\DSL\{ + bool_entry, + datetime_entry, + df, + enum_entry, + float_entry, + from_array, + from_rows, + int_entry, + json_entry, + list_entry, + map_entry, + ref, + row, + rows, + str_entry, + string_entry, + struct_element, + struct_entry, + struct_type, + type_int, + type_list, + type_map, + type_string, + xml_entry}; use Flow\ETL\Tests\Fixtures\Enum\BackedStringEnum; use Flow\ETL\Tests\Integration\IntegrationTestCase; use Flow\ETL\{Extractor, FlowContext, Rows}; @@ -29,7 +53,7 @@ public function extract(FlowContext $context) : \Generator bool_entry('deleted', false), datetime_entry('created-at', new \DateTimeImmutable('2020-07-13 15:00')), str_entry('phase', null), - array_entry( + json_entry( 'array', [ ['id' => 1, 'status' => 'NEW'], @@ -54,7 +78,6 @@ public function extract(FlowContext $context) : \Generator struct_element('name', type_string()), ]) ), - object_entry('object', new \ArrayIterator([1, 2, 3])), enum_entry('enum', BackedStringEnum::three), xml_entry('xml', 'testbar'), ), @@ -66,15 +89,15 @@ enum_entry('enum', BackedStringEnum::three), self::assertSame( <<<'ASCIITABLE' -+------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+----------------------+-------+----------------------+ -| id | price | 100 | deleted | created-at | phase | array | list | map | items | object | enum | xml | -+------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+----------------------+-------+----------------------+ -| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | ArrayIterator Object | three | | -| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | ArrayIterator Object | three | | -| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | ArrayIterator Object | three | | -| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | ArrayIterator Object | three | | -| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | ArrayIterator Object | three | | -+------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+----------------------+-------+----------------------+ ++------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+-------+----------------------+ +| id | price | 100 | deleted | created-at | phase | array | list | map | items | enum | xml | ++------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+-------+----------------------+ +| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | three | | +| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | three | | +| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | three | | +| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | three | | +| 1234 | 123.45 | 100 | false | 2020-07-13T15:00:00+ | | [{"id":1,"status":"N | [1,2,3] | ["NEW","PENDING"] | {"item-id":"1","name | three | | ++------+--------+-----+---------+----------------------+-------+----------------------+---------+-------------------+----------------------+-------+----------------------+ 5 rows ASCIITABLE, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/GroupByTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/GroupByTest.php index 1ebe2931a..c32138290 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/GroupByTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/GroupByTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Integration\DataFrame; -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ average, count, datetime_entry, @@ -18,6 +18,7 @@ from_rows, int_entry, int_schema, + json_entry, list_schema, lit, max, @@ -43,14 +44,14 @@ public function test_group_by_array() : void { $rows = df() ->read(from_rows(rows( - row(int_entry('id', 1), int_entry('score', 20), array_entry('array', ['a', 'b', 'c', 'd'])), - row(int_entry('id', 2), int_entry('score', 20), array_entry('array', ['a', 'b', 'c', 'd'])), - row(int_entry('id', 3), int_entry('score', 25), array_entry('array', ['a', 'b', 'c'])), - row(int_entry('id', 4), int_entry('score', 30), array_entry('array', ['a', 'b', 'c'])), - row(int_entry('id', 5), int_entry('score', 40), array_entry('array', ['a', 'b'])), - row(int_entry('id', 6), int_entry('score', 40), array_entry('array', ['a', 'b'])), - row(int_entry('id', 7), int_entry('score', 45), array_entry('array', ['a', 'b'])), - row(int_entry('id', 9), int_entry('score', 50), array_entry('array', ['a'])), + row(int_entry('id', 1), int_entry('score', 20), json_entry('array', ['a', 'b', 'c', 'd'])), + row(int_entry('id', 2), int_entry('score', 20), json_entry('array', ['a', 'b', 'c', 'd'])), + row(int_entry('id', 3), int_entry('score', 25), json_entry('array', ['a', 'b', 'c'])), + row(int_entry('id', 4), int_entry('score', 30), json_entry('array', ['a', 'b', 'c'])), + row(int_entry('id', 5), int_entry('score', 40), json_entry('array', ['a', 'b'])), + row(int_entry('id', 6), int_entry('score', 40), json_entry('array', ['a', 'b'])), + row(int_entry('id', 7), int_entry('score', 45), json_entry('array', ['a', 'b'])), + row(int_entry('id', 9), int_entry('score', 50), json_entry('array', ['a'])), ))) ->groupBy('array') ->aggregate(sum('score'), average('score')) diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/LimitTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/LimitTest.php index b083e3e09..a4a5e1d5b 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/LimitTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/LimitTest.php @@ -4,9 +4,18 @@ namespace Flow\ETL\Tests\Integration\DataFrame; -use function Flow\ETL\DSL\{df, from_array, from_rows, ref}; +use function Flow\ETL\DSL\{df, + from_array, + from_rows, + ref, + struct_element, + type_int, + type_list, + type_map, + type_string, + type_structure}; use Flow\ETL\Exception\InvalidArgumentException; -use Flow\ETL\Row\Entry\{ArrayEntry, IntegerEntry}; +use Flow\ETL\Row\Entry\{IntegerEntry}; use Flow\ETL\Tests\Integration\IntegrationTestCase; use Flow\ETL\{Extractor, FlowContext, Row, Rows}; @@ -124,11 +133,24 @@ public function extract(FlowContext $context) : \Generator { for ($i = 0; $i < 1000; $i++) { yield new Rows( - Row::create(new ArrayEntry('ids', [ - ['id' => $i + 1, 'more_ids' => [['more_id' => $i + 4], ['more_id' => $i + 7]]], - ['id' => $i + 2, 'more_ids' => [['more_id' => $i + 5], ['more_id' => $i + 8]]], - ['id' => $i + 3, 'more_ids' => [['more_id' => $i + 6], ['more_id' => $i + 9]]], - ])), + Row::create( + new Row\Entry\ListEntry( + 'ids', + [ + ['id' => $i + 1, 'more_ids' => [['more_id' => $i + 4], ['more_id' => $i + 7]]], + ['id' => $i + 2, 'more_ids' => [['more_id' => $i + 5], ['more_id' => $i + 8]]], + ['id' => $i + 3, 'more_ids' => [['more_id' => $i + 6], ['more_id' => $i + 9]]], + ], + type_list( + type_structure( + [ + struct_element('id', type_int()), + struct_element('more_ids', type_list(type_map(type_string(), type_int()))), + ] + ) + ) + ), + ) ); } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/RenameTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/RenameTest.php index d2d664bd9..be1b61470 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/RenameTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/RenameTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Integration\DataFrame; -use function Flow\ETL\DSL\{array_entry, bool_entry, df, from_rows, int_entry, ref, str_entry}; +use function Flow\ETL\DSL\{bool_entry, df, from_rows, int_entry, json_entry, ref, str_entry}; use Flow\ETL\Tests\Integration\IntegrationTestCase; use Flow\ETL\Transformer\StyleConverter\StringStyles; use Flow\ETL\{Row, Rows}; @@ -37,8 +37,8 @@ public function test_rename() : void public function test_rename_all() : void { $rows = new Rows( - Row::create(array_entry('array', ['id' => 1, 'name' => 'name', 'active' => true])), - Row::create(array_entry('array', ['id' => 2, 'name' => 'name', 'active' => false])) + Row::create(json_entry('array', ['id' => 1, 'name' => 'name', 'active' => true])), + Row::create(json_entry('array', ['id' => 2, 'name' => 'name', 'active' => false])) ); $ds = df() diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/AddJsonTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/AddJsonTest.php index 320efca4a..606a125d0 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/AddJsonTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/AddJsonTest.php @@ -68,7 +68,7 @@ public function test_adding_json_as_object_from_string_entry() : void [ [ 'id' => 1, - 'json' => '{"id":1,"name":"test"}', + 'json' => ['id' => 1, 'name' => 'test'], ], ], $memory->dump() @@ -89,7 +89,7 @@ public function test_adding_json_from_string_entry() : void [ [ 'id' => 1, - 'json' => '[{"id":1},{"id":2}]', + 'json' => [['id' => 1], ['id' => 2]], ], ], $memory->dump() diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/CountTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/CountTest.php index 904414fd5..3733535a3 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/CountTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/CountTest.php @@ -54,28 +54,4 @@ public function test_count_on_non_countable() : void $memory->dump() ); } - - public function test_count_on_object() : void - { - $iterator = new \ArrayIterator([1, 2, 3]); - - (new Flow()) - ->read( - from_array( - [ - ['key' => $iterator], - ] - ) - ) - ->withEntry('count', ref('key')->size()) - ->write(to_memory($memory = new ArrayMemory())) - ->run(); - - self::assertEquals( - [ - ['key' => $iterator, 'count' => 3], - ], - $memory->dump() - ); - } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/HashTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/HashTest.php index d8ddb534a..18292269a 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/HashTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/HashTest.php @@ -16,7 +16,6 @@ final class HashTest extends TestCase public static function provideValues() : \Generator { yield 'array' => [[1, 2, 3], 'f1c4574435e8e2806215a6b677d5e06b']; - yield 'object' => [new \stdClass(), 'f2ba00ab9bfb5c37e41fed64ffe5ea8a']; yield 'string' => ['value', 'd7ab8cce59abd5050d59506fb013961a']; } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/JsonEncodeTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/JsonEncodeTest.php index 1a96dc1a8..dbf8fff47 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/JsonEncodeTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/Function/JsonEncodeTest.php @@ -26,7 +26,7 @@ public function test_adding_json_as_object_from_string_entry() : void [ [ 'id' => 1, - 'json' => '{"id":1,"name":"test"}', + 'json' => ['id' => 1, 'name' => 'test'], ], ], $memory->dump() @@ -48,7 +48,7 @@ public function test_adding_json_from_string_entry() : void [ [ 'id' => 1, - 'json' => '[1,2,3]', + 'json' => [1, 2, 3], ], ], $memory->dump() diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/DataFrameTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/DataFrameTest.php index c88d28532..45e31f2d8 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/DataFrameTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/DataFrameTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit; -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ average, bool_entry, compare_entries_by_name_desc, @@ -14,6 +14,7 @@ from_array, from_rows, int_entry, + json_entry, lit, ref, refs, @@ -510,7 +511,7 @@ public function test_selective_validation_against_schema() : void $rows = (new Flow())->process( new Rows( Row::create(int_entry('id', 1), str_entry('name', 'foo'), bool_entry('active', true)), - Row::create(int_entry('id', 2), str_entry('name', null), array_entry('tags', ['foo', 'bar'])), + Row::create(int_entry('id', 2), str_entry('name', null), json_entry('tags', ['foo', 'bar'])), Row::create(int_entry('id', 2), str_entry('name', 'bar'), bool_entry('active', false)), ) )->validate( @@ -521,7 +522,7 @@ public function test_selective_validation_against_schema() : void self::assertEquals( new Rows( Row::create(int_entry('id', 1), str_entry('name', 'foo'), bool_entry('active', true)), - Row::create(int_entry('id', 2), str_entry('name', null), array_entry('tags', ['foo', 'bar'])), + Row::create(int_entry('id', 2), str_entry('name', null), json_entry('tags', ['foo', 'bar'])), Row::create(int_entry('id', 2), str_entry('name', 'bar'), bool_entry('active', false)), ), $rows diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/DateIntervalFunctionsTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/DateIntervalFunctionsTest.php new file mode 100644 index 000000000..167663129 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/DateIntervalFunctionsTest.php @@ -0,0 +1,72 @@ + new \DateInterval('P1D'), 'seconds' => 86400, 'milliseconds' => 86400000, 'microseconds' => 86400000000]; + yield ['interval' => new \DateInterval('PT1H'), 'seconds' => 3600, 'milliseconds' => 3600000, 'microseconds' => 3600000000]; + yield ['interval' => new \DateInterval('PT1M'), 'seconds' => 60, 'milliseconds' => 60000, 'microseconds' => 60000000]; + yield ['interval' => new \DateInterval('PT1S'), 'seconds' => 1, 'milliseconds' => 1000, 'microseconds' => 1000000]; + yield ['interval' => new \DateInterval('P1DT1H1M1S'), 'seconds' => 90061, 'milliseconds' => 90061000, 'microseconds' => 90061000000]; + yield ['interval' => new \DateInterval('P1DT1H1M1S'), 'seconds' => 90061, 'milliseconds' => 90061000, 'microseconds' => 90061000000]; + + // Edge cases + // Fractional seconds are ignored in conversion to seconds + $fractionalSeconds = new \DateInterval('PT1S'); + $fractionalSeconds->f = 0.4; + yield ['interval' => $fractionalSeconds, 'seconds' => 2, 'milliseconds' => 1400, 'microseconds' => 1400000]; + + // Inverted interval + $invertedInterval = new \DateInterval('P1D'); + $invertedInterval->invert = 1; + yield ['interval' => $invertedInterval, 'seconds' => -86400, 'milliseconds' => -86400000, 'microseconds' => -86400000000]; + + // Zero values + yield ['interval' => new \DateInterval('PT0S'), 'seconds' => 0, 'milliseconds' => 0, 'microseconds' => 0]; + + // Large values + yield ['interval' => new \DateInterval('P1000D'), 'seconds' => 86400000, 'milliseconds' => 86400000000, 'microseconds' => 86400000000000]; + } + + #[TestWith([new \DateInterval('P1Y'), "Relative DateInterval (with months/years) can't be converted to microseconds."])] + #[TestWith([new \DateInterval('P1M'), "Relative DateInterval (with months/years) can't be converted to microseconds."])] + public function test_converting_relative_date_intervals_to_microseconds(\DateInterval $interval, string $exceptionMessage) : void + { + $this->expectExceptionMessage($exceptionMessage); + date_interval_to_microseconds($interval); + } + + #[TestWith([new \DateInterval('P1Y'), "Relative DateInterval (with months/years) can't be converted to milliseconds."])] + #[TestWith([new \DateInterval('P1M'), "Relative DateInterval (with months/years) can't be converted to milliseconds."])] + public function test_converting_relative_date_intervals_to_milliseconds(\DateInterval $interval, string $exceptionMessage) : void + { + $this->expectExceptionMessage($exceptionMessage); + date_interval_to_milliseconds($interval); + } + + #[TestWith([new \DateInterval('P1Y'), "Relative DateInterval (with months/years) can't be converted to seconds."])] + #[TestWith([new \DateInterval('P1M'), "Relative DateInterval (with months/years) can't be converted to seconds."])] + public function test_converting_relative_date_intervals_to_seconds(\DateInterval $interval, string $exceptionMessage) : void + { + $this->expectExceptionMessage($exceptionMessage); + date_interval_to_seconds($interval); + } + + #[DataProvider('date_interval_provider')] + public function test_date_interval_to_milliseconds(\DateInterval $interval, int $seconds, int $milliseconds, int $microseconds) : void + { + self::assertEquals($seconds, date_interval_to_seconds($interval)); + self::assertEquals($milliseconds, date_interval_to_milliseconds($interval)); + self::assertEquals($microseconds, date_interval_to_microseconds($interval)); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Extractor/SequenceExtractorTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Extractor/SequenceExtractorTest.php index 2bd2b66f7..faac92e7e 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Extractor/SequenceExtractorTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Extractor/SequenceExtractorTest.php @@ -4,7 +4,11 @@ namespace Flow\ETL\Tests\Unit\Extractor; -use function Flow\ETL\DSL\{datetime_entry, float_entry, from_sequence_date_period, from_sequence_date_period_recurrences, from_sequence_number}; +use function Flow\ETL\DSL\{date_entry, + float_entry, + from_sequence_date_period, + from_sequence_date_period_recurrences, + from_sequence_number}; use Flow\ETL\{Config, FlowContext, Row, Rows}; use PHPUnit\Framework\TestCase; @@ -16,15 +20,15 @@ public function test_extracting_from_date_period() : void self::assertEquals( [ - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-02')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-03')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-04')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-05')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-06')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-07')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-08')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-09')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-10')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-02')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-03')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-04')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-05')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-06')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-07')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-08')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-09')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-10')))), ], \iterator_to_array($extractor->extract(new FlowContext(Config::default()))) ); @@ -36,16 +40,16 @@ public function test_extracting_from_date_period_recurrences() : void self::assertEquals( [ - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-02')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-03')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-04')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-05')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-06')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-07')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-08')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-09')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-10')))), - new Rows(Row::create(datetime_entry('day', new \DateTimeImmutable('2023-01-11')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-02')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-03')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-04')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-05')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-06')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-07')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-08')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-09')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-10')))), + new Rows(Row::create(date_entry('day', new \DateTimeImmutable('2023-01-11')))), ], \iterator_to_array($extractor->extract(new FlowContext(Config::default()))) ); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayExpandTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayExpandTest.php index b0b2efcda..a141be24e 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayExpandTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayExpandTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_expand, int_entry, ref}; +use function Flow\ETL\DSL\{array_expand, int_entry, json_entry, ref}; use Flow\ETL\Function\ArrayExpand\ArrayExpand; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -14,7 +14,7 @@ final class ArrayExpandTest extends TestCase public function test_expand_both() : void { $row = Row::create( - array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), + json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), ); self::assertSame( @@ -30,7 +30,7 @@ public function test_expand_both() : void public function test_expand_keys() : void { $row = Row::create( - array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), + json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), ); self::assertSame( @@ -42,7 +42,7 @@ public function test_expand_keys() : void public function test_expand_values() : void { $row = Row::create( - array_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), + json_entry('array', ['a' => 1, 'b' => 2, 'c' => 3]), ); self::assertSame( diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetCollectionTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetCollectionTest.php index b81f3d090..0cbdbdea5 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetCollectionTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetCollectionTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_get_collection, array_get_collection_first, int_entry, ref}; +use function Flow\ETL\DSL\{array_get_collection, array_get_collection_first, int_entry, json_entry, ref}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -22,7 +22,7 @@ public function test_for_not_array_entry() : void public function test_getting_keys_from_simple_array() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ 'id' => 1, @@ -39,7 +39,7 @@ public function test_getting_keys_from_simple_array() : void public function test_getting_specific_keys_from_collection_of_array() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ [ @@ -70,7 +70,7 @@ public function test_getting_specific_keys_from_collection_of_array() : void public function test_getting_specific_keys_from_first_element_in_collection_of_array() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ [ @@ -102,7 +102,7 @@ public function test_getting_specific_keys_from_first_element_in_collection_of_a public function test_getting_specific_keys_from_first_element_in_collection_of_array_when_first_index_does_not_exists() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ 2 => [ diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetTest.php index 66ec6abfd..8b465f7ff 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayGetTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_exists, array_get, int_entry, ref}; +use function Flow\ETL\DSL\{array_exists, array_get, int_entry, json_entry, ref}; use Flow\ArrayDot\Exception\InvalidPathException; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -20,7 +20,7 @@ public function test_array_access_for_not_array_entry() : void public function test_array_accessor_transformer() : void { $row = Row::create( - array_entry('array_entry', [ + json_entry('array_entry', [ 'id' => 1, 'status' => 'PENDING', 'enabled' => true, @@ -34,7 +34,7 @@ public function test_array_accessor_transformer() : void public function test_array_accessor_transformer_with_invalid_and_without_strict_path() : void { $row = Row::create( - array_entry('array_entry', [ + json_entry('array_entry', [ 'id' => 1, 'status' => 'PENDING', 'enabled' => true, @@ -54,7 +54,7 @@ public function test_array_accessor_transformer_with_invalid_but_strict_path() : array_get(ref('array_entry'), 'invalid_path')->eval( Row::create( - array_entry('array_entry', [ + json_entry('array_entry', [ 'id' => 1, 'status' => 'PENDING', 'enabled' => true, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeyRenameTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeyRenameTest.php index 7e7a605bc..7ab7cc9b0 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeyRenameTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeyRenameTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_key_rename, int_entry, ref}; +use function Flow\ETL\DSL\{array_key_rename, int_entry, json_entry, ref}; use Flow\ArrayDot\Exception\InvalidPathException; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -23,11 +23,11 @@ public function test_for_not_array_entry() : void public function test_renames_array_entry_keys_in_multiple_array_entry() : void { $row = Row::create( - array_entry('customer', [ + json_entry('customer', [ 'first' => 'John', 'last' => 'Snow', ]), - array_entry('shipping', [ + json_entry('shipping', [ 'address' => [ 'line' => '3644 Clement Street', 'city' => 'Atalanta', @@ -60,7 +60,7 @@ public function test_renames_array_entry_keys_in_multiple_array_entry() : void public function test_renames_array_entry_keys_in_single_array_entry() : void { $row = Row::create( - array_entry('array_entry', [ + json_entry('array_entry', [ 'id' => 1, 'status' => 'PENDING', 'enabled' => true, @@ -82,7 +82,7 @@ public function test_renames_array_entry_keys_in_single_array_entry() : void public function test_throws_exception_for_invalid_path() : void { $row = Row::create( - array_entry('array_entry', [ + json_entry('array_entry', [ 'id' => 1, 'status' => 'PENDING', 'enabled' => true, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeysStyleConverterTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeysStyleConverterTest.php index 2a39b805b..d50f9b668 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeysStyleConverterTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayKeysStyleConverterTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_keys_style_convert, int_entry, ref}; +use function Flow\ETL\DSL\{array_keys_style_convert, int_entry, json_entry, ref}; use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -17,7 +17,7 @@ public function test_for_invalid_style() : void $this->expectExceptionMessage('Unrecognized style invalid, please use one of following:'); $row = Row::create( - array_entry('invalid_entry', []), + json_entry('invalid_entry', []), ); array_keys_style_convert(ref('invalid_entry'), 'invalid')->eval($row); @@ -35,7 +35,7 @@ public function test_for_not_array_entry() : void public function test_transforms_case_style_for_all_keys_in_array_entry() : void { $row = Row::create( - new Row\Entry\ArrayEntry( + new Row\Entry\JsonEntry( 'arrayEntry', [ 'itemId' => 1, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeCollectionTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeCollectionTest.php index cc7d79064..3719579bb 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeCollectionTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeCollectionTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, array_merge_collection, int_entry, ref}; +use function Flow\ETL\DSL\{array_merge_collection, int_entry, json_entry, ref}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -13,7 +13,7 @@ final class ArrayMergeCollectionTest extends TestCase public function test_attempt_of_merging_collection_where_not_every_element_is_array() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ ['foo' => 'bar'], @@ -37,7 +37,7 @@ public function test_for_not_array_entry() : void public function test_merging_collection_of_arrays() : void { $row = Row::create( - array_entry( + json_entry( 'array_entry', [ [ diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeTest.php index c23f6102f..a737a9e85 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayMergeTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, int_entry, lit, ref}; +use function Flow\ETL\DSL\{int_entry, json_entry, lit, ref}; use Flow\ETL\Function\ArrayMerge; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -18,8 +18,8 @@ public function test_array_merge_two_array_row_entries() : void ref('a')->arrayMerge(ref('b')) ->eval( Row::create( - array_entry('a', ['a' => 1]), - array_entry('b', ['b' => 2]), + json_entry('a', ['a' => 1]), + json_entry('b', ['b' => 2]), ), ) ); @@ -42,7 +42,7 @@ public function test_array_merge_when_left_side_is_not_an_array() : void ->eval( Row::create( int_entry('a', 1), - array_entry('b', ['b' => 2]), + json_entry('b', ['b' => 2]), ), ) ); @@ -54,7 +54,7 @@ public function test_array_merge_when_right_side_is_not_an_array() : void ref('a')->arrayMerge(ref('b')) ->eval( Row::create( - array_entry('a', ['a' => 1]), + json_entry('a', ['a' => 1]), int_entry('b', 2), ), ) diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayReverseTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayReverseTest.php index 4fa9ef574..86554f9e3 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayReverseTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayReverseTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, int_entry, ref}; +use function Flow\ETL\DSL\{int_entry, json_entry, ref}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -17,7 +17,7 @@ public function test_array_reverse_array_entry() : void ref('a')->arrayReverse() ->eval( Row::create( - array_entry('a', [4, 10, 3, 5]), + json_entry('a', [4, 10, 3, 5]), ), ) ); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArraySortTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArraySortTest.php index b3ee1d03b..e77ce3663 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArraySortTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArraySortTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, ref, str_entry}; +use function Flow\ETL\DSL\{json_entry, ref, str_entry}; use Flow\ETL\Function\ArraySort\Sort; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -14,8 +14,8 @@ final class ArraySortTest extends TestCase public function test_sorting_big_arrays() : void { self::assertSame( - ref('array')->arraySort()->eval(Row::create(array_entry('array', \json_decode($this->jsonDifferentOrder(), true, 512, JSON_THROW_ON_ERROR)))), - ref('array')->arraySort()->eval(Row::create(array_entry('array', \json_decode($this->json(), true, 512, JSON_THROW_ON_ERROR)))) + ref('array')->arraySort()->eval(Row::create(json_entry('array', \json_decode($this->jsonDifferentOrder(), true, 512, JSON_THROW_ON_ERROR)))), + ref('array')->arraySort()->eval(Row::create(json_entry('array', \json_decode($this->json(), true, 512, JSON_THROW_ON_ERROR)))) ); } @@ -32,7 +32,7 @@ public function test_sorting_nested_array_using_asort_algo() : void ], ], ref('array')->arraySort(Sort::asort)->eval(Row::create( - array_entry( + json_entry( 'array', [ 'a' => [ @@ -61,7 +61,7 @@ public function test_sorting_nested_associative_array() : void ], ], ref('array')->arraySort(Sort::ksort)->eval(Row::create( - array_entry( + json_entry( 'array', [ 'a' => [ diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayUnpackTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayUnpackTest.php index aa0baa308..73ea0253a 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayUnpackTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ArrayUnpackTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, int_entry, ref}; +use function Flow\ETL\DSL\{int_entry, json_entry, ref}; use Flow\ETL\Function\ArrayUnpack; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -15,7 +15,7 @@ public function test_array_unpack() : void { $row = Row::create( int_entry('id', 1), - array_entry('array_entry', [ + json_entry('array_entry', [ 'status' => 'PENDING', 'enabled' => true, 'array' => ['foo' => 'bar'], diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/BinaryComparisonsTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/BinaryComparisonsTest.php index 0b1d1a630..5fa5b96ee 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/BinaryComparisonsTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/BinaryComparisonsTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, datetime_entry, int_entry, lit, ref, str_entry, type_string}; +use function Flow\ETL\DSL\{datetime_entry, int_entry, json_entry, lit, ref, str_entry, type_string}; use Flow\ETL\Function\{Contains, EndsWith, Equals, GreaterThan, GreaterThanEqual, IsIn, IsNotNull, IsNotNumeric, IsNull, IsNumeric, IsType, LessThan, LessThanEqual, NotEquals, NotSame, Same, StartsWith}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -56,8 +56,8 @@ public function test_greater_than() : void public function test_is_in() : void { $row = Row::with( - array_entry('a', [1, 2, 3, 4, 5]), - array_entry('b', ['a', 'b', 'c']), + json_entry('a', [1, 2, 3, 4, 5]), + json_entry('b', ['a', 'b', 'c']), str_entry('c', 'another'), int_entry('d', 4), str_entry('e', 'b'), diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ConcatTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ConcatTest.php index 2be5973f0..5019f6f7e 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ConcatTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ConcatTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, concat, lit, ref}; +use function Flow\ETL\DSL\{concat, json_entry, lit, ref}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -14,7 +14,7 @@ public function test_concat_arrays() : void { self::assertSame( '["a"]["b","c"]', - concat(ref('array_1'), ref('array_2'))->eval(Row::create(array_entry('array_1', ['a']), array_entry('array_2', ['b', 'c']))), + concat(ref('array_1'), ref('array_2'))->eval(Row::create(json_entry('array_1', ['a']), json_entry('array_2', ['b', 'c']))), ); } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/HashTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/HashTest.php index 44ac9a4d1..36daadcaf 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/HashTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/HashTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, concat, datetime_entry, hash, lit, ref, str_entry}; +use function Flow\ETL\DSL\{concat, datetime_entry, hash, json_entry, lit, ref, str_entry}; use Flow\ETL\Hash\NativePHPHash; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -15,7 +15,7 @@ public function test_hashing_array_value() : void { self::assertSame( '4450cf82dc53848e2bbe9798b70b0a6a', - ref('value')->hash()->eval(Row::create(array_entry('value', ['test']))), + ref('value')->hash()->eval(Row::create(json_entry('value', ['test']))), ); } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/JsonEncodeTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/JsonEncodeTest.php index 38192f4ec..80e139e28 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/JsonEncodeTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/JsonEncodeTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, datetime_entry, int_entry, ref, str_entry}; +use function Flow\ETL\DSL\{datetime_entry, int_entry, json_entry, ref, str_entry}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -38,7 +38,7 @@ public function test_json_encode_on_valid_associative_array() : void { self::assertSame( '{"value":1}', - ref('value')->jsonEncode()->eval(Row::create(array_entry('value', ['value' => 1]))), + ref('value')->jsonEncode()->eval(Row::create(json_entry('value', ['value' => 1]))), ); } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/NotTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/NotTest.php index 8dca2c45f..9d32fccb6 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/NotTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/NotTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, int_entry, lit, not, ref}; +use function Flow\ETL\DSL\{int_entry, json_entry, lit, not, ref}; use Flow\ETL\Row; use PHPUnit\Framework\TestCase; @@ -27,7 +27,7 @@ public function test_not_expression_on_boolean_true_value() : void public function test_not_expression_on_is_in_expression() : void { self::assertTrue( - not(ref('value')->isIn(ref('array')))->eval(Row::create(array_entry('array', [1, 2, 3]), int_entry('value', 10))) + not(ref('value')->isIn(ref('array')))->eval(Row::create(json_entry('array', [1, 2, 3]), int_entry('value', 10))) ); } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/OnEachTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/OnEachTest.php index 34b2e96e2..b18a82d7c 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/OnEachTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/OnEachTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{array_entry, ref, row, type_string}; +use function Flow\ETL\DSL\{json_entry, ref, row, type_string}; use Flow\ETL\Adapter\Elasticsearch\Tests\Integration\TestCase; final class OnEachTest extends TestCase @@ -16,7 +16,7 @@ public function test_executing_function_on_each_value_from_array() : void ref('array')->onEach(ref('element')->cast(type_string())) ->eval( row( - array_entry( + json_entry( 'array', [1, 2, 3, 4, 5] ) @@ -32,7 +32,7 @@ public function test_executing_function_on_each_value_from_empty_array() : void ref('array')->onEach(ref('element')->cast(type_string())) ->eval( row( - array_entry( + json_entry( 'array', [] ) @@ -48,7 +48,7 @@ public function test_executing_function_on_each_value_with_preserving_keys() : v ref('array')->onEach(ref('element')->cast(type_string()), true) ->eval( row( - array_entry( + json_entry( 'array', ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5] ) @@ -64,7 +64,7 @@ public function test_executing_function_on_each_value_without_preserving_keys() ref('array')->onEach(ref('element')->cast(type_string()), false) ->eval( row( - array_entry( + json_entry( 'array', ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5] ) diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ParameterTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ParameterTest.php index ec52ae0ab..ac48fc91a 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ParameterTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Function/ParameterTest.php @@ -4,24 +4,12 @@ namespace Flow\ETL\Tests\Unit\Function; -use function Flow\ETL\DSL\{object_entry, ref, row, str_entry, type_boolean, type_int, type_object, type_string}; +use function Flow\ETL\DSL\{ref, row, str_entry, type_boolean, type_int, type_string}; use Flow\ETL\Function\Parameter; use PHPUnit\Framework\TestCase; final class ParameterTest extends TestCase { - public function test_as_object() : void - { - $parameter = new Parameter(ref('value')); - - $value = new \DateTimeImmutable(); - - self::assertNull($parameter->as(row(object_entry('value', $value)), type_int())); - self::assertSame($value, $parameter->as(row(object_entry('value', $value)), type_object(\DateTimeInterface::class))); - self::assertSame($value, $parameter->as(row(object_entry('value', $value)), type_object(\DateTimeImmutable::class))); - self::assertNull($parameter->as(row(object_entry('value', $value)), type_object(\DateTime::class))); - } - public function test_as_one_of() : void { $parameter = new Parameter(ref('value')); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/DateCastingHandlerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/DateCastingHandlerTest.php new file mode 100644 index 000000000..53518375a --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/DateCastingHandlerTest.php @@ -0,0 +1,31 @@ + ['2021-01-01 00:00:00', new \DateTimeImmutable('2021-01-01 00:00:00')]; + yield 'int' => [1609459200, new \DateTimeImmutable('2021-01-01 00:00:00')]; + yield 'float' => [1609459200.0, new \DateTimeImmutable('2021-01-01 00:00:00')]; + yield 'bool' => [true, new \DateTimeImmutable('1970-01-01 00:00:00')]; + yield 'DateTimeInterface' => [new \DateTimeImmutable('2021-01-01 15:00:00'), new \DateTimeImmutable('2021-01-01 00:00:00')]; + yield 'DateInterval' => [new \DateInterval('P1D'), new \DateTimeImmutable('1970-01-02 00:00:00')]; + yield 'DOMElement' => [new \DOMElement('element', '2021-01-01 12:32:00'), new \DateTimeImmutable('2021-01-01 00:00:00')]; + } + + #[DataProvider('date_castable_data_provider')] + public function test_casting_different_data_types_to_date(mixed $value, \DateTimeImmutable $expected) : void + { + self::assertEquals($expected, (new DateCastingHandler())->value($value, type_date(), Caster::default())); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/TimeCastingHandlerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/TimeCastingHandlerTest.php new file mode 100644 index 000000000..772bf24b3 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Caster/TimeCastingHandlerTest.php @@ -0,0 +1,28 @@ + ['PT1S', new \DateInterval('PT1S')]; + yield 'datetime' => [new \DateTimeImmutable('2021-01-01 00:00:01'), new \DateInterval('PT1S')]; + yield 'date' => [new \DateTimeImmutable('2021-01-01'), new \DateInterval('PT0S')]; + yield 'time' => [new \DateInterval('PT10S'), new \DateInterval('PT10S')]; + } + + #[DataProvider('time_castable_data_provider')] + public function test_casting_different_time_types_to_time(mixed $value, \DateInterval $expextedInterval) : void + { + self::assertEquals($expextedInterval, (new TimeCastingHandler())->value($value, type_time(), Caster::default())); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Logical/TimeTypeTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Logical/TimeTypeTest.php new file mode 100644 index 000000000..bb3d6e071 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/Logical/TimeTypeTest.php @@ -0,0 +1,57 @@ +isEqual(type_time()) + ); + self::assertFalse( + type_time()->isEqual(type_int()) + ); + } + + public function test_is_valid() : void + { + self::assertTrue(type_time(true)->isValid(null)); + self::assertTrue(type_time()->isValid(new \DateInterval('PT10S'))); + self::assertFalse(type_time()->isValid('00:00:01')); + self::assertFalse(type_time()->isValid('PT10S')); + } + + public function test_merge_non_nullable_with_non_nullable() : void + { + self::assertFalse(type_time()->merge(type_time())->nullable()); + } + + public function test_merge_non_nullable_with_nullable() : void + { + self::assertTrue(type_time()->merge(type_time(true))->nullable()); + self::assertTrue(type_time(true)->merge(type_time(false))->nullable()); + } + + public function test_merge_nullable_with_nullable() : void + { + self::assertTrue(type_time(true)->merge(type_time(true))->nullable()); + } + + public function test_to_string() : void + { + self::assertSame( + 'time', + type_time()->toString() + ); + self::assertSame( + '?time', + type_time(true)->toString() + ); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeDetectorTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeDetectorTest.php index a9deb7fe8..fb7209e1d 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeDetectorTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeDetectorTest.php @@ -5,10 +5,12 @@ namespace Flow\ETL\Tests\Unit\PHP\Type; use Flow\ETL\PHP\Type\Logical\{DateTimeType, + DateType, JsonType, ListType, MapType, StructureType, + TimeType, UuidType, XMLElementType, XMLType}; @@ -35,6 +37,18 @@ public static function provide_logical_types_data() : \Generator 'json', ]; + yield 'time' => [ + new \DateInterval('PT1H'), + TimeType::class, + 'time', + ]; + + yield 'date' => [ + new \DateTime('2024-01-01'), + DateType::class, + 'date', + ]; + yield 'datetime' => [ new \DateTime(), DateTimeType::class, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeFactoryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeFactoryTest.php index 5831c076f..9bfe6d97b 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeFactoryTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Type/TypeFactoryTest.php @@ -20,6 +20,7 @@ type_resource, type_string, type_structure, + type_time, type_uuid, type_xml, type_xml_element}; @@ -133,4 +134,22 @@ public function test_normalizing_and_creating_xml_type() : void $xml = type_xml(); self::assertEquals($xml, TypeFactory::fromArray($xml->normalize())); } + + public function test_normalizing_date() : void + { + $date = type_datetime(); + self::assertEquals($date, TypeFactory::fromArray($date->normalize())); + } + + public function test_normalizing_date_time() : void + { + $dateTime = type_datetime(); + self::assertEquals($dateTime, TypeFactory::fromArray($dateTime->normalize())); + } + + public function test_normalizing_time() : void + { + $time = type_time(); + self::assertEquals($time, TypeFactory::fromArray($time->normalize())); + } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Value/UuidTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Value/UuidTest.php new file mode 100644 index 000000000..a557a5585 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/PHP/Value/UuidTest.php @@ -0,0 +1,78 @@ +expectException(InvalidArgumentException::class); + new Uuid('invalid-uuid-string'); + } + + public function test_construct_with_ramsey_uuid_instance() : void + { + $ramseyUuid = RamseyUuid::uuid4(); + $uuid = new Uuid($ramseyUuid); + + self::assertSame($ramseyUuid->toString(), $uuid->toString()); + } + + public function test_construct_with_symfony_uuid_instance() : void + { + $symfonyUuid = SymfonyUuid::v4(); + $uuid = new Uuid($symfonyUuid); + + self::assertSame($symfonyUuid->toRfc4122(), $uuid->toString()); + } + + public function test_construct_with_valid_string_uuid() : void + { + $uuidString = '123e4567-e89b-12d3-a456-426614174000'; + $uuid = new Uuid($uuidString); + + self::assertSame($uuidString, $uuid->toString()); + } + + public function test_from_string_creates_instance() : void + { + $uuidString = '123e4567-e89b-12d3-a456-426614174000'; + $uuid = Uuid::fromString($uuidString); + + self::assertInstanceOf(Uuid::class, $uuid); + self::assertSame($uuidString, $uuid->toString()); + } + + public function test_is_equal_with_different_uuid() : void + { + $uuid1 = new Uuid('123e4567-e89b-12d3-a456-426614174000'); + $uuid2 = new Uuid('123e4567-e89b-12d3-a456-426614174001'); + + self::assertFalse($uuid1->isEqual($uuid2)); + } + + public function test_is_equal_with_same_uuid() : void + { + $uuidString = '123e4567-e89b-12d3-a456-426614174000'; + $uuid1 = new Uuid($uuidString); + $uuid2 = new Uuid($uuidString); + + self::assertTrue($uuid1->isEqual($uuid2)); + } + + public function test_to_string_returns_correct_value() : void + { + $uuidString = '123e4567-e89b-12d3-a456-426614174000'; + $uuid = new Uuid($uuidString); + + self::assertSame($uuidString, (string) $uuid); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Comparator/WeakObjectComparatorTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Comparator/WeakObjectComparatorTest.php deleted file mode 100644 index 791875a90..000000000 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Comparator/WeakObjectComparatorTest.php +++ /dev/null @@ -1,42 +0,0 @@ -equals($row, $nextRow)); - } - - public function test_compare_rows_with_other_different_entries() : void - { - $row = Row::create( - new Row\Entry\ObjectEntry('object', new \ArrayObject([])), - new Row\Entry\StringEntry('string', 'test'), - ); - $nextRow = Row::create( - new Row\Entry\ObjectEntry('object', new \ArrayObject([])), - new Row\Entry\StringEntry('string', 'test1'), - ); - - $comparator = new Row\Comparator\WeakObjectComparator(); - - self::assertFalse($comparator->equals($row, $nextRow)); - } -} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ArrayEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ArrayEntryTest.php deleted file mode 100644 index a3a523343..000000000 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ArrayEntryTest.php +++ /dev/null @@ -1,141 +0,0 @@ - [ - true, - new ArrayEntry('name', [1, 2, 3]), - new ArrayEntry('name', [1, 2, 3]), - ]; - yield 'equal names and equal simple integerrish arrays with the same order' => [ - false, - new ArrayEntry('name', [1, 2, 3]), - new ArrayEntry('name', ['1', '2', '3']), - ]; - yield 'equal names and equal simple integer arrays with different order' => [ - true, - new ArrayEntry('name', [1, 2, 3]), - new ArrayEntry('name', [2, 1, 3]), - ]; - yield 'equal names and equal simple string arrays with the same order' => [ - true, - new ArrayEntry('name', ['aaa', 'bbb', 'ccc']), - new ArrayEntry('name', ['aaa', 'bbb', 'ccc']), - ]; - yield 'equal names and equal simple string arrays with the same order but different characters size' => [ - false, - new ArrayEntry('name', ['aaa', 'bbb', 'ccc']), - new ArrayEntry('name', ['aaa', 'BBB', 'ccc']), - ]; - yield 'equal names and equal multi dimensional array with the same order' => [ - true, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'bar'], 'baz']), - ]; - yield 'equal names and equal multi dimensional array with different order' => [ - true, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['baz', 'bar' => ['bar' => 'bar', 'foo' => 'foo'], 'foo' => 1]), - ]; - yield 'equal names and equal multi dimensional array with missing entry' => [ - false, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['baz', 'bar' => ['bar' => 'bar'], 'foo' => 1]), - ]; - yield 'equal names and equal multi dimensional array with different characters size' => [ - false, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => 'foo', 'bar' => 'BAR'], 'baz']), - ]; - yield 'equal names and equal multi dimensional array with object same entries' => [ - true, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => $date = new \DateTimeImmutable('2020-01-01 00:00:00'), 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => $date = new \DateTimeImmutable('2020-01-01 00:00:00'), 'bar' => 'bar'], 'baz']), - ]; - yield 'equal names and equal multi dimensional array with object different entries' => [ - false, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new \DateTimeImmutable('2020-01-01 00:00:00'), 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new \DateTimeImmutable('2020-01-05 00:00:00'), 'bar' => 'bar'], 'baz']), - ]; - yield 'equal names and equal multi dimensional array with equals different entries' => [ - true, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new \stdClass(), 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new \stdClass(), 'bar' => 'bar'], 'baz']), - ]; - yield 'equal names and equal multi dimensional array with equals different entries 1' => [ - true, - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new IntegerEntry('test', 1), 'bar' => 'bar'], 'baz']), - new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new IntegerEntry('test', 1), 'bar' => 'bar'], 'baz']), - ]; - } - - public function test_entry_name_can_be_zero() : void - { - self::assertSame('0', (new ArrayEntry('0', ['id' => 1]))->name()); - } - - #[DataProvider('is_equal_data_provider')] - public function test_is_equal(bool $equals, ArrayEntry $entry, ArrayEntry $nextEntry) : void - { - self::assertSame($equals, $entry->isEqual($nextEntry)); - } - - public function test_map() : void - { - $entry = new ArrayEntry('entry-name', ['id' => 1, 'name' => 'one']); - - self::assertEquals( - $entry, - $entry->map(fn (array $value) => $value) - ); - } - - public function test_prevents_from_creating_entry_with_empty_entry_name() : void - { - $this->expectExceptionMessage('Entry name cannot be empty'); - - new ArrayEntry('', ['id' => 1]); - } - - public function test_renames_entry() : void - { - $entry = new ArrayEntry('entry-name', ['id' => 1, 'name' => 'one']); - $newEntry = $entry->rename('new-entry-name'); - - self::assertEquals('new-entry-name', $newEntry->name()); - self::assertEquals(['id' => 1, 'name' => 'one'], $newEntry->value()); - } - - public function test_returns_array_as_value() : void - { - $items = [ - ['item-id' => 1, 'name' => 'one'], - ['item-id' => 2, 'name' => 'two'], - ['item-id' => 3, 'name' => 'three'], - ]; - $entry = new ArrayEntry('items', $items); - - self::assertEquals($items, $entry->value()); - } - - public function test_serialization() : void - { - $string = new ArrayEntry('name', ['foo' => 1, 'bar' => ['foo' => new IntegerEntry('test', 1), 'bar' => 'bar'], 'baz']); - - $serialized = \serialize($string); - /** @var ArrayEntry $unserialized */ - $unserialized = \unserialize($serialized); - - self::assertTrue($string->isEqual($unserialized)); - } -} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/DateEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/DateEntryTest.php new file mode 100644 index 000000000..182ad10d4 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/DateEntryTest.php @@ -0,0 +1,97 @@ + [true, new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00'))]; + yield 'different names and values' => [false, new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00')), new DateEntry('different_name', new \DateTimeImmutable('2020-01-01 00:00:00+00'))]; + yield 'equal names and different values day' => [false, new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-01-02 00:00:00+00'))]; + yield 'equal names and different values tz' => [false, new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+10'))]; + yield 'different names characters and equal values' => [false, new DateEntry('NAME', new \DateTimeImmutable('2020-01-01 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00'))]; + yield 'equal names and equal values and different format' => [false, new DateEntry('name', new \DateTimeImmutable('2020-02-19 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-01-02 00:00:00+00'))]; + yield 'equal names and equal values for given format' => [true, new DateEntry('name', new \DateTimeImmutable('2020-02-19 00:00:00+00')), new DateEntry('name', new \DateTimeImmutable('2020-02-19 00:00:00+00'))]; + } + + public function test_entry_name_can_be_zero() : void + { + self::assertSame('0', (new DateEntry('0', new \DateTimeImmutable('2020-07-13 12:00')))->name()); + } + + public function test_invalid_date() : void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid value given: 'random string', reason: Failed to parse time string (random string) at position 0 (r): The timezone could not be found in the database"); + + new DateEntry('a', 'random string'); + } + + #[DataProvider('is_equal_data_provider')] + public function test_is_equal(bool $equals, DateEntry $entry, DateEntry $nextEntry) : void + { + self::assertEquals($equals, $entry->isEqual($nextEntry)); + } + + public function test_map() : void + { + $entry = new DateEntry('entry-name', new \DateTimeImmutable()); + + self::assertEquals( + $entry, + $entry->map(fn (\DateTimeImmutable $dateTimeImmutable) => $dateTimeImmutable) + ); + } + + public function test_prevents_from_creating_entry_with_empty_entry_name() : void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Entry name cannot be empty'); + + new DateEntry('', new \DateTimeImmutable('2020-07-13 12:00')); + } + + public function test_removes_time() : void + { + self::assertEquals( + (new DateEntry('entry-name', new \DateTimeImmutable('2020-07-13 12:00')))->value(), + new \DateTimeImmutable('2020-07-13 00:00') + ); + self::assertEquals( + (new DateEntry('entry-name', '2020-07-13 12:00'))->value(), + new \DateTimeImmutable('2020-07-13 00:00') + ); + self::assertEquals( + (new DateEntry('entry-name', new \DateTime('2020-07-13 12:00')))->value(), + new \DateTimeImmutable('2020-07-13 00:00') + ); + } + + public function test_renames_entry() : void + { + $entry = new DateEntry('entry-name', new \DateTimeImmutable()); + $newEntry = $entry->rename('new-entry-name'); + + self::assertEquals('new-entry-name', $newEntry->name()); + self::assertEquals($entry->value(), $newEntry->value()); + } + + public function test_serialization() : void + { + $string = new DateEntry('name', new \DateTimeImmutable('2020-01-01 00:00:00+00')); + + $serialized = \serialize($string); + /** @var DateEntry $unserialized */ + $unserialized = \unserialize($serialized); + + self::assertTrue($string->isEqual($unserialized)); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonEntryTest.php index bec425376..7f289ed8e 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonEntryTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonEntryTest.php @@ -85,8 +85,8 @@ public function test_empty_entry() : void $jsonEntry = new JsonEntry('empty', []); $jsonObjectEntry = JsonEntry::object('empty', []); - self::assertEquals('[]', $jsonEntry->value()); - self::assertEquals('{}', $jsonObjectEntry->value()); + self::assertEquals([], $jsonEntry->value()); + self::assertEquals([], $jsonObjectEntry->value()); } public function test_entry_name_can_be_zero() : void @@ -115,26 +115,22 @@ public function test_map() : void ['item-id' => 2, 'name' => 'two'], ['item-id' => 3, 'name' => 'three'], ]; - $entry = (new JsonEntry('items', $items))->map(function (string $value) { - $trimValue = \json_decode($value, true, 512, JSON_THROW_ON_ERROR); - - \array_walk_recursive($trimValue, function (&$v) : void { + $entry = (new JsonEntry('items', $items))->map(function (array $value) { + \array_walk_recursive($value, function (&$v) : void { if (\is_string($v)) { $v = \trim($v); } }); - return \json_encode($trimValue, JSON_THROW_ON_ERROR); + return \json_encode($value, JSON_THROW_ON_ERROR); }); self::assertEquals( - \json_encode( - $items = [ - ['item-id' => 1, 'name' => 'one', 'address' => ['line1' => 'NO. 47 HENGSHAN ROAD, ECONOMIC TECHNOLOGICAL DEVELOPMENT ZONE, WUHU, ANHUI, 241000, CHINA']], - ['item-id' => 2, 'name' => 'two'], - ['item-id' => 3, 'name' => 'three'], - ] - ), + $items = [ + ['item-id' => 1, 'name' => 'one', 'address' => ['line1' => 'NO. 47 HENGSHAN ROAD, ECONOMIC TECHNOLOGICAL DEVELOPMENT ZONE, WUHU, ANHUI, 241000, CHINA']], + ['item-id' => 2, 'name' => 'two'], + ['item-id' => 3, 'name' => 'three'], + ], $entry->value() ); } @@ -173,7 +169,7 @@ public function test_returns_json_as_value() : void ]; $entry = new JsonEntry('items', $items); - self::assertEquals(\json_encode($items), $entry->value()); + self::assertEquals(\json_encode($items), $entry->toString()); } public function test_serialization() : void diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonObjectEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonObjectEntryTest.php index d1136879f..10b16f6a8 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonObjectEntryTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/JsonObjectEntryTest.php @@ -63,22 +63,21 @@ public function test_is_equal(bool $equals, JsonEntry $entry, JsonEntry $nextEnt public function test_map() : void { $item = ['item-id' => 1, 'name' => 'one']; - $entry = (JsonEntry::object('item', $item))->map(function (string $value) { - $jsonValue = \json_decode($value, true, 512, JSON_THROW_ON_ERROR); - \array_walk($jsonValue, function (&$v) : void { + $entry = (JsonEntry::object('item', $item))->map(function (array $value) { + \array_walk($value, function (&$v) : void { if (\is_string($v)) { $v = \mb_strtoupper($v); } }); - return \json_encode($jsonValue, JSON_THROW_ON_ERROR); + return \json_encode($value, JSON_THROW_ON_ERROR); }); self::assertEquals( \json_encode( ['item-id' => 1, 'name' => 'ONE'] ), - $entry->value() + $entry->toString() ); } @@ -96,6 +95,6 @@ public function test_returns_json_as_value() : void $item = ['item-id' => 1, 'name' => 'one']; $entry = JsonEntry::object('item', $item); - self::assertEquals(\json_encode($item), $entry->value()); + self::assertEquals(\json_encode($item), $entry->toString()); } } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ObjectEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ObjectEntryTest.php deleted file mode 100644 index 97cd11006..000000000 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/ObjectEntryTest.php +++ /dev/null @@ -1,68 +0,0 @@ - [true, new ObjectEntry('name', $object = new \stdClass()), new ObjectEntry('name', $object)]; - yield 'different names and values' => [false, new ObjectEntry('name', $object = new \stdClass()), new ObjectEntry('different_name', $object)]; - yield 'equal names and different values' => [false, new ObjectEntry('name', new \stdClass()), new ObjectEntry('name', new \ArrayObject())]; - yield 'different names characters and equal values' => [false, new ObjectEntry('NAME', $object = new \stdClass()), new ObjectEntry('name', $object)]; - } - - public function test_entry_name_can_be_zero() : void - { - self::assertSame('0', (new ObjectEntry('0', new \stdClass()))->name()); - } - - #[DataProvider('is_equal_data_provider')] - public function test_is_equal(bool $equals, ObjectEntry $entry, ObjectEntry $nextEntry) : void - { - self::assertSame($equals, $entry->isEqual($nextEntry)); - } - - public function test_map() : void - { - $entry = new ObjectEntry('entry-name', new \stdClass()); - - self::assertEquals( - $entry, - $entry->map(fn (\stdClass $object) => $object) - ); - } - - public function test_prevents_from_creating_entry_with_empty_entry_name() : void - { - $this->expectExceptionMessage('Entry name cannot be empty'); - - new ObjectEntry('', new \stdClass()); - } - - public function test_renames_entry() : void - { - $entry = new ObjectEntry('entry-name', new \stdClass()); - $newEntry = $entry->rename('new-entry-name'); - - self::assertEquals('new-entry-name', $newEntry->name()); - self::assertIsObject($newEntry->value()); - } - - public function test_serialization() : void - { - $string = new ObjectEntry('name', $object = new \stdClass()); - - $serialized = \serialize($string); - /** @var ObjectEntry $unserialized */ - $unserialized = \unserialize($serialized); - - self::assertTrue($string->isEqual($unserialized)); - } -} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/TimeEntryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/TimeEntryTest.php new file mode 100644 index 000000000..b54d013c4 --- /dev/null +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Entry/TimeEntryTest.php @@ -0,0 +1,137 @@ + [true, new TimeEntry('name', new \DateInterval('PT10S')), new TimeEntry('name', new \DateInterval('PT10S'))]; + yield 'different names and values' => [false, new TimeEntry('name', new \DateInterval('PT10S')), new TimeEntry('different_name', new \DateInterval('PT20S'))]; + yield 'equal names and different values day' => [false, new TimeEntry('name', new \DateInterval('PT1S')), new TimeEntry('name', new \DateInterval('PT2S'))]; + yield 'different names characters and equal values' => [false, new TimeEntry('NAME', new \DateInterval('P1D')), new TimeEntry('name', new \DateInterval('P1D'))]; + } + + public function test_creating_from_days() : void + { + self::assertEquals( + new \DateInterval('P1D'), + TimeEntry::fromDays('time', 1)->value() + ); + } + + public function test_creating_from_hours() : void + { + self::assertEquals( + new \DateInterval('PT1H'), + TimeEntry::fromHours('time', 1)->value() + ); + } + + public function test_creating_from_microseconds() : void + { + self::assertEquals( + new \DateInterval('PT1S'), + TimeEntry::fromMicroseconds('time', 1000000)->value() + ); + } + + public function test_creating_from_milliseconds() : void + { + self::assertEquals( + new \DateInterval('PT1S'), + TimeEntry::fromMilliseconds('time', 1000)->value() + ); + } + + public function test_creating_from_minutes() : void + { + self::assertEquals( + new \DateInterval('PT1M'), + TimeEntry::fromMinutes('time', 1)->value() + ); + } + + public function test_creating_from_seconds() : void + { + self::assertEquals( + new \DateInterval('PT1S'), + TimeEntry::fromSeconds('time', 1)->value() + ); + } + + public function test_creating_from_time_string() : void + { + $timeEntry = new TimeEntry('name', '00:01:23'); + + self::assertEquals( + new TimeEntry('name', new \DateInterval('PT1M23S')), + $timeEntry + ); + } + + public function test_entry_name_can_be_zero() : void + { + self::assertSame('0', (new TimeEntry('0', new \DateInterval('PT10S')))->name()); + } + + #[DataProvider('is_equal_data_provider')] + public function test_is_equal(bool $equals, TimeEntry $entry, TimeEntry $nextEntry) : void + { + self::assertEquals($equals, $entry->isEqual($nextEntry)); + } + + public function test_map() : void + { + $entry = new TimeEntry('entry-name', new \DateInterval('PT10S')); + + self::assertEquals( + $entry, + $entry->map(fn (\DateInterval $time) => $time) + ); + } + + public function test_prevents_from_creating_entry_with_empty_entry_name() : void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Entry name cannot be empty'); + + new TimeEntry('', new \DateInterval('P1D')); + } + + public function test_renames_entry() : void + { + $entry = new TimeEntry('entry-name', new \DateInterval('P1D')); + $newEntry = $entry->rename('new-entry-name'); + + self::assertEquals('new-entry-name', $newEntry->name()); + self::assertEquals($entry->value(), $newEntry->value()); + } + + public function test_serialization() : void + { + $string = new TimeEntry('name', new \DateInterval('P1D')); + + $serialized = \serialize($string); + /** @var TimeEntry $unserialized */ + $unserialized = \unserialize($serialized); + + self::assertTrue($string->isEqual($unserialized)); + } + + public function test_to_string() : void + { + self::assertEquals('00:01:00', TimeEntry::fromMinutes('time', 1)->toString()); + self::assertEquals('00:00:00.001000', TimeEntry::fromMilliseconds('time', 1)->toString()); + self::assertEquals('00:00:00.000001', TimeEntry::fromMicroseconds('time', 1)->toString()); + self::assertEquals('10:00:00', TimeEntry::fromHours('time', 10)->toString()); + self::assertEquals('26:12:31', TimeEntry::fromString('time', 'P1DT2H12M31S')->toString()); + } +} diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Factory/NativeEntryFactoryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Factory/NativeEntryFactoryTest.php index 4d3a264c3..7f77e6672 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Factory/NativeEntryFactoryTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Factory/NativeEntryFactoryTest.php @@ -4,8 +4,8 @@ namespace Flow\ETL\Tests\Unit\Row\Factory; -use function Flow\ETL\DSL\{array_entry, - bool_entry, +use function Flow\ETL\DSL\{bool_entry, + date_entry, datetime_entry, enum_entry, float_entry, @@ -13,16 +13,15 @@ enum_entry, json_entry, json_object_entry, list_entry, - object_entry, str_entry, structure_element, structure_type, + time_entry, type_datetime, type_float, type_int, type_list, type_map, - type_object, type_string, uuid_entry, xml_entry}; @@ -30,7 +29,7 @@ enum_entry, use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; use Flow\ETL\PHP\Type\Logical\{ListType, StructureType}; -use Flow\ETL\Row\Entry\StructureEntry; +use Flow\ETL\Row\Entry\{StructureEntry, TimeEntry}; use Flow\ETL\Row\Factory\NativeEntryFactory; use Flow\ETL\Row\Schema; use Flow\ETL\Tests\Fixtures\Enum\BackedIntEnum; @@ -79,15 +78,6 @@ public function test_array_structure() : void ); } - public function test_array_with_schema() : void - { - self::assertEquals( - array_entry('e', [1, 2, 3]), - (new NativeEntryFactory()) - ->create('e', [1, 2, 3], new Schema(Schema\Definition::array('e'))) - ); - } - public function test_bool() : void { self::assertEquals( @@ -104,6 +94,38 @@ public function test_boolean_with_schema() : void ); } + public function test_date() : void + { + self::assertEquals( + date_entry('e', '2022-01-01'), + (new NativeEntryFactory())->create('e', new \DateTimeImmutable('2022-01-01')) + ); + } + + public function test_date_from_int_with_definition() : void + { + self::assertEquals( + date_entry('e', '1970-01-01'), + (new NativeEntryFactory())->create('e', 1, new Schema(Schema\Definition::date('e'))) + ); + } + + public function test_date_from_null_with_definition() : void + { + self::assertEquals( + date_entry('e', null), + (new NativeEntryFactory())->create('e', null, new Schema(Schema\Definition::date('e', true))) + ); + } + + public function test_date_from_string_with_definition() : void + { + self::assertEquals( + date_entry('e', '2022-01-01'), + (new NativeEntryFactory())->create('e', '2022-01-01', new Schema(Schema\Definition::date('e'))) + ); + } + public function test_datetime() : void { self::assertEquals( @@ -316,19 +338,9 @@ public function test_nested_structure() : void public function test_object() : void { - self::assertEquals( - object_entry('e', $object = new \ArrayIterator([1, 2])), - (new NativeEntryFactory())->create('e', $object) - ); - } + $this->expectExceptionMessage("e: object can't be converted to any known Entry, please normalize that object first"); - public function test_object_with_schema() : void - { - self::assertEquals( - object_entry('e', $object = new \ArrayObject([1, 2, 3])), - (new NativeEntryFactory()) - ->create('e', $object, new Schema(Schema\Definition::object('e', type_object($object::class)))) - ); + (new NativeEntryFactory())->create('e', new \ArrayIterator([1, 2])); } public function test_string() : void @@ -364,6 +376,30 @@ public function test_structure() : void ); } + public function test_time() : void + { + self::assertEquals( + TimeEntry::fromDays('e', 1), + (new NativeEntryFactory())->create('e', new \DateInterval('P1D')) + ); + } + + public function test_time_from_null_with_definition() : void + { + self::assertEquals( + time_entry('e', null), + (new NativeEntryFactory())->create('e', null, new Schema(Schema\Definition::time('e', true))) + ); + } + + public function test_time_from_string_with_definition() : void + { + self::assertEquals( + time_entry('e', new \DateInterval('P10D')), + (new NativeEntryFactory())->create('e', 'P10D', new Schema(Schema\Definition::time('e'))) + ); + } + #[DataProvider('provide_unrecognized_data')] public function test_unrecognized_data_set_same_as_provided(string $input) : void { diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/DefinitionTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/DefinitionTest.php index 617e42463..e4949df9f 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/DefinitionTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/DefinitionTest.php @@ -109,6 +109,14 @@ public function test_merging_anything_and_string() : void ); } + public function test_merging_date_with_datetime() : void + { + self::assertEquals( + Definition::datetime('datetime'), + Definition::datetime('datetime')->merge(Definition::date('datetime')) + ); + } + public function test_merging_different_entries() : void { $this->expectException(RuntimeException::class); @@ -137,10 +145,26 @@ public function test_merging_numeric_types() : void ); } + public function test_merging_time_with_date() : void + { + self::assertEquals( + Definition::datetime('datetime'), + Definition::date('datetime')->merge(Definition::time('datetime')) + ); + } + + public function test_merging_time_with_datetime() : void + { + self::assertEquals( + Definition::datetime('datetime'), + Definition::datetime('datetime')->merge(Definition::time('datetime')) + ); + } + public function test_merging_two_different_lists() : void { self::assertEquals( - Definition::array('list'), + Definition::json('list'), Definition::list('list', type_list(type_string()))->merge(Definition::list('list', type_list(type_int()))) ); } @@ -148,7 +172,7 @@ public function test_merging_two_different_lists() : void public function test_merging_two_different_maps() : void { self::assertEquals( - Definition::array('map'), + Definition::json('map'), Definition::map('map', type_map(type_string(), type_string()))->merge(Definition::map('map', type_map(type_string(), type_int()))) ); } @@ -156,7 +180,7 @@ public function test_merging_two_different_maps() : void public function test_merging_two_different_structures() : void { self::assertEquals( - Definition::array('structure'), + Definition::json('structure'), Definition::structure( 'structure', struct_type([ diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/Formatter/ASCIISchemaFormatterTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/Formatter/ASCIISchemaFormatterTest.php index b3ab2398f..1e26a42d7 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/Formatter/ASCIISchemaFormatterTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/Formatter/ASCIISchemaFormatterTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Row\Schema\Formatter; -use function Flow\ETL\DSL\{type_int, type_string}; +use function Flow\ETL\DSL\{type_int, type_list, type_string}; use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\Map\{MapKey, MapValue}; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; @@ -36,7 +36,7 @@ public function test_format_nested_schema() : void ]), ), Schema\Definition::string('name', nullable: true), - Schema\Definition::array('tags'), + Schema\Definition::list('tags', type_list(type_string())), Schema\Definition::boolean('active'), Schema\Definition::xml('xml'), Schema\Definition::xml_element('xml_element'), @@ -58,7 +58,7 @@ public function test_format_nested_schema() : void | |-- city: ?string | |-- country: ?string |-- name: ?string -|-- tags: array +|-- tags: list |-- active: boolean |-- xml: xml |-- xml_element: xml_element @@ -75,7 +75,7 @@ public function test_format_schema() : void { $schema = new Schema( Schema\Definition::string('name', nullable: true), - Schema\Definition::array('tags'), + Schema\Definition::list('tags', type_list(type_string())), Schema\Definition::boolean('active'), Schema\Definition::xml('xml'), Schema\Definition::map('map', new MapType(MapKey::string(), MapValue::string())), @@ -86,7 +86,7 @@ public function test_format_schema() : void <<<'SCHEMA' schema |-- name: ?string -|-- tags: array +|-- tags: list |-- active: boolean |-- xml: xml |-- map: map diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/StrictValidatorTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/StrictValidatorTest.php index 034cb9335..42dcb2e3b 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/StrictValidatorTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/Schema/StrictValidatorTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Row\Schema; -use function Flow\ETL\DSL\{bool_entry, int_entry, str_entry}; +use function Flow\ETL\DSL\{bool_entry, int_entry, str_entry, type_list, type_string}; use Flow\ETL\Row\Schema; use Flow\ETL\Row\Schema\StrictValidator; use Flow\ETL\{Row, Rows}; @@ -49,7 +49,7 @@ public function test_rows_with_an_extra_entry() : void Schema\Definition::integer('id'), Schema\Definition::string('name'), Schema\Definition::boolean('active'), - Schema\Definition::array('tags'), + Schema\Definition::list('tags', type_list(type_string())), ); self::assertFalse( diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/SchemaMergeTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/SchemaMergeTest.php index 8078ff7af..2da3abba3 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/SchemaMergeTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Row/SchemaMergeTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Row; -use function Flow\ETL\DSL\{bool_schema, datetime_schema, float_schema, int_schema, object_schema, schema, str_schema, type_object}; +use function Flow\ETL\DSL\{bool_schema, datetime_schema, float_schema, int_schema, schema, str_schema}; use PHPUnit\Framework\TestCase; final class SchemaMergeTest extends TestCase @@ -143,10 +143,6 @@ public function test_nullable_with_non_nullable_schema() : void schema(float_schema('col', nullable: true)), schema(float_schema('col'))->merge(schema(float_schema('col', nullable: true))) ); - self::assertEquals( - schema(object_schema('col', type_object(\stdClass::class, nullable: true))), - schema(object_schema('col', type_object(\stdClass::class)))->merge(schema(object_schema('col', type_object(\stdClass::class, nullable: true)))) - ); self::assertEquals( schema(datetime_schema('col', nullable: true)), schema(datetime_schema('col'))->merge(schema(datetime_schema('col', nullable: true))) diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/RowTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/RowTest.php index 00288ac63..771dbcffd 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/RowTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/RowTest.php @@ -4,15 +4,15 @@ namespace Flow\ETL\Tests\Unit; -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ bool_entry, datetime_entry, float_entry, generate_random_int, int_entry, + json_entry, list_entry, map_entry, - object_entry, row, str_entry, struct_element, @@ -21,13 +21,19 @@ type_int, type_list, type_map, - type_object, type_string}; use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\Map\{MapKey, MapValue}; use Flow\ETL\PHP\Type\Logical\Structure\StructureElement; use Flow\ETL\PHP\Type\Logical\{ListType, MapType, StructureType}; -use Flow\ETL\Row\Entry\{ArrayEntry, BooleanEntry, DateTimeEntry, IntegerEntry, MapEntry, StringEntry, StructureEntry}; +use Flow\ETL\Row\Entry\{ + BooleanEntry, + DateTimeEntry, + IntegerEntry, + JsonEntry, + MapEntry, + StringEntry, + StructureEntry}; use Flow\ETL\Row\Schema; use Flow\ETL\Row\Schema\Definition; use PHPUnit\Framework\Attributes\DataProvider; @@ -54,8 +60,8 @@ public static function is_equal_data_provider() : \Generator ]; yield 'simple same array entries' => [ true, - row(new ArrayEntry('json', ['foo' => ['bar' => 'baz']])), - row(new ArrayEntry('json', ['foo' => ['bar' => 'baz']])), + row(new JsonEntry('json', ['foo' => ['bar' => 'baz']])), + row(new JsonEntry('json', ['foo' => ['bar' => 'baz']])), ]; yield 'simple same collection entries' => [ true, @@ -101,7 +107,7 @@ public function test_getting_schema_from_row() : void bool_entry('deleted', false), datetime_entry('created-at', new \DateTimeImmutable('now')), str_entry('phase', null), - array_entry( + json_entry( 'array', [ ['id' => 1, 'status' => 'NEW'], @@ -122,7 +128,6 @@ public function test_getting_schema_from_row() : void ['NEW', 'PENDING'], type_map(type_int(), type_string()) ), - object_entry('object', new \ArrayIterator([1, 2, 3])) ); self::assertEquals( @@ -132,7 +137,7 @@ public function test_getting_schema_from_row() : void Definition::boolean('deleted'), Definition::dateTime('created-at'), Definition::string('phase', nullable: true), - Definition::array('array'), + Definition::json('array'), Definition::structure( 'items', new StructureType([ @@ -145,7 +150,6 @@ public function test_getting_schema_from_row() : void new MapType(MapKey::integer(), MapValue::string()) ), Definition::list('list', new ListType(ListElement::integer())), - Definition::object('object', type_object(\ArrayIterator::class)), ), $row->schema() ); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/RowsTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/RowsTest.php index 459f5c541..d80ba1f6f 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/RowsTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/RowsTest.php @@ -4,13 +4,24 @@ namespace Flow\ETL\Tests\Unit; -use function Flow\ETL\DSL\{array_entry, bool_entry, datetime_entry, int_entry, list_entry, ref, row, rows, rows_partitioned, str_entry, type_int, type_list, type_string}; +use function Flow\ETL\DSL\{bool_entry, + datetime_entry, + int_entry, + list_entry, + ref, + row, + rows, + rows_partitioned, + str_entry, + type_int, + type_list, + type_string}; use function Flow\Filesystem\DSL\{partition, partitions}; use Flow\ETL\Exception\{InvalidArgumentException, RuntimeException}; use Flow\ETL\PHP\Type\Logical\List\ListElement; use Flow\ETL\PHP\Type\Logical\ListType; -use Flow\ETL\Row\Comparator\{NativeComparator, WeakObjectComparator}; -use Flow\ETL\Row\Entry\{BooleanEntry, DateTimeEntry, ObjectEntry, StringEntry}; +use Flow\ETL\Row\Comparator\{NativeComparator}; +use Flow\ETL\Row\Entry\{BooleanEntry, DateTimeEntry, StringEntry}; use Flow\ETL\Row\Schema\Definition; use Flow\ETL\Row\{Comparator, Schema}; use Flow\ETL\{Row, Rows}; @@ -95,15 +106,6 @@ public static function unique_rows_provider() : \Generator ), $comparator = new NativeComparator(), ]; - - yield 'simple identical rows with objects' => [ - $expected = rows(row(new ObjectEntry('object', new \stdClass()))), - $notUnique = rows( - row(new ObjectEntry('object', $object = new \stdClass())), - row(new ObjectEntry('object', $object = new \stdClass())) - ), - $comparator = new WeakObjectComparator(), - ]; } public function test_adding_multiple_rows() : void @@ -816,7 +818,7 @@ public function test_rows_schema() : void $rows = rows( row(int_entry('id', 1), str_entry('name', 'foo')), row(int_entry('id', 1), str_entry('name', null), list_entry('list', [1, 2], type_list(type_int()))), - row(int_entry('id', 1), str_entry('name', 'bar'), array_entry('tags', ['a', 'b'])), + row(int_entry('id', 1), str_entry('name', 'bar'), list_entry('tags', ['a', 'b'], type_list(type_string()))), row(int_entry('id', 1), int_entry('name', 25)), ); @@ -824,7 +826,7 @@ public function test_rows_schema() : void new Schema( Definition::integer('id'), Definition::string('name', true), - Definition::array('tags', false, true), + Definition::list('tags', new ListType(ListElement::string(), true)), Definition::list('list', new ListType(ListElement::integer(), true)) ), $rows->schema() @@ -839,7 +841,7 @@ public function test_rows_schema_when_rows_have_different_list_types() : void ); self::assertEquals( - new Schema(Definition::array('list')), + new Schema(Definition::json('list')), $rows->schema() ); } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/AutoCastTransformerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/AutoCastTransformerTest.php index 0a61724ae..fd2525186 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/AutoCastTransformerTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/AutoCastTransformerTest.php @@ -33,7 +33,7 @@ public function test_transforming_row() : void 'integer' => 1, 'float' => 1.0, 'boolean' => true, - 'json' => '{"foo":"bar"}', + 'json' => ['foo' => 'bar'], 'datetime' => new \DateTimeImmutable('2021-01-01 00:00:00'), 'null' => null, 'nil' => null, diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/DropEntriesTransformerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/DropEntriesTransformerTest.php index cca210de6..e3ee627ab 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/DropEntriesTransformerTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/DropEntriesTransformerTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Transformer; -use function Flow\ETL\DSL\{array_entry, int_entry, row, rows, string_entry}; +use function Flow\ETL\DSL\{int_entry, json_entry, row, rows, string_entry}; use Flow\ETL\Transformer\DropEntriesTransformer; use Flow\ETL\{Config, FlowContext}; use PHPUnit\Framework\TestCase; @@ -17,7 +17,7 @@ public function test_dropping_entries() : void row( int_entry('id', 1), string_entry('name', 'Row Name'), - array_entry('array', ['test']) + json_entry('array', ['test']) ) ); @@ -36,7 +36,7 @@ public function test_removing_not_existing_entries() : void row( int_entry('id', 1), string_entry('name', 'Row Name'), - array_entry('array', ['test']) + json_entry('array', ['test']) ) ); diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/OrderEntriesTransformerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/OrderEntriesTransformerTest.php index 12f51c6c5..874de59df 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/OrderEntriesTransformerTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/OrderEntriesTransformerTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Transformer; -use function Flow\ETL\DSL\{array_entry, +use function Flow\ETL\DSL\{ bool_entry, compare_entries_by_name, compare_entries_by_name_desc, @@ -21,7 +21,6 @@ enum_entry, json_entry, list_entry, map_entry, - object_entry, row, rows, str_entry, @@ -58,13 +57,6 @@ public function test_ordering_entries_by_name_and_type() : void str_entry('string_b', 'string'), uuid_entry('uuid', new \Flow\ETL\PHP\Value\Uuid(Uuid::uuid4())), json_entry('json', ['id' => 1, 'status' => 'NEW']), - array_entry( - 'array', - [ - ['id' => 1, 'status' => 'NEW'], - ['id' => 2, 'status' => 'PENDING'], - ] - ), list_entry('list', [1, 2, 3], type_list(type_int())), map_entry('map', [0 => 'zero', 1 => 'one', 2 => 'two'], type_map(type_int(), type_string())), struct_entry( @@ -90,14 +82,13 @@ public function test_ordering_entries_by_name_and_type() : void ), ]), ), - object_entry('object', new \ArrayIterator([1, 2, 3])), enum_entry('enum_a', BackedStringEnum::three), enum_entry('enum_b', BackedStringEnum::one) ) ); self::assertSame( - ['uuid', 'int_a', 'int_b', 'bool', 'bool_a', 'bool_c', 'float_a', 'float_b', 'datetime_d', 'datetime_z', 'string_a', 'string_b', 'enum_a', 'enum_b', 'array', 'list', 'json', 'map', 'object', 'struct'], + ['uuid', 'int_a', 'int_b', 'bool', 'bool_a', 'bool_c', 'float_a', 'float_b', 'datetime_d', 'datetime_z', 'string_a', 'string_b', 'enum_a', 'enum_b', 'list', 'json', 'map', 'struct'], \array_keys((new OrderEntriesTransformer(compare_entries_by_type_and_name()))->transform($rows, flow_context(config()))->toArray()[0]) ); } @@ -148,13 +139,6 @@ public function test_ordering_entries_by_type() : void str_entry('null', null), uuid_entry('uuid', new \Flow\ETL\PHP\Value\Uuid(Uuid::uuid4())), json_entry('json', ['id' => 1, 'status' => 'NEW']), - array_entry( - 'array', - [ - ['id' => 1, 'status' => 'NEW'], - ['id' => 2, 'status' => 'PENDING'], - ] - ), list_entry('list', [1, 2, 3], type_list(type_int())), map_entry('map', [0 => 'zero', 1 => 'one', 2 => 'two'], type_map(type_int(), type_string())), struct_entry( @@ -180,17 +164,16 @@ public function test_ordering_entries_by_type() : void ), ]), ), - object_entry('object', new \ArrayIterator([1, 2, 3])), enum_entry('enum', BackedStringEnum::three) ) ); self::assertSame( - ['uuid', 'int', 'bool', 'float', 'datetime', 'null', 'enum', 'array', 'list', 'json', 'map', 'object', 'struct'], + ['uuid', 'int', 'bool', 'float', 'datetime', 'null', 'enum', 'list', 'json', 'map', 'struct'], \array_keys((new OrderEntriesTransformer(compare_entries_by_type()))->transform($rows, flow_context(config()))->toArray()[0]) ); self::assertSame( - array_reverse(['uuid', 'int', 'bool', 'float', 'datetime', 'null', 'enum', 'array', 'list', 'json', 'map', 'object', 'struct']), + array_reverse(['uuid', 'int', 'bool', 'float', 'datetime', 'null', 'enum', 'list', 'json', 'map', 'struct']), \array_keys((new OrderEntriesTransformer(compare_entries_by_type_desc()))->transform($rows, flow_context(config()))->toArray()[0]) ); } diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/RenameEntryTransformerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/RenameEntryTransformerTest.php index bf787a7e1..17e6ce8c7 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/RenameEntryTransformerTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/RenameEntryTransformerTest.php @@ -23,9 +23,7 @@ public function test_renaming_entries() : void new Row\Entry\StringEntry('status', 'PENDING'), new Row\Entry\BooleanEntry('enabled', true), new Row\Entry\DateTimeEntry('datetime', new \DateTimeImmutable('2020-01-01 00:00:00 UTC')), - new Row\Entry\ArrayEntry('array', ['foo', 'bar']), new Row\Entry\JsonEntry('json', ['foo', 'bar']), - new Row\Entry\ObjectEntry('object', new \stdClass()), new Row\Entry\StringEntry('null', null) ), ), @@ -41,9 +39,7 @@ public function test_renaming_entries() : void new Row\Entry\StringEntry('status', 'PENDING'), new Row\Entry\BooleanEntry('enabled', true), new Row\Entry\DateTimeEntry('datetime', new \DateTimeImmutable('2020-01-01 00:00:00 UTC')), - new Row\Entry\ArrayEntry('array', ['foo', 'bar']), new Row\Entry\JsonEntry('json', ['foo', 'bar']), - new Row\Entry\ObjectEntry('object', new \stdClass()), new Row\Entry\IntegerEntry('new_int', 1000), new Row\Entry\StringEntry('nothing', null) ), diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/SelectEntriesTransformerTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/SelectEntriesTransformerTest.php index 03b62184a..8a2a74259 100644 --- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/SelectEntriesTransformerTest.php +++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Transformer/SelectEntriesTransformerTest.php @@ -4,7 +4,7 @@ namespace Flow\ETL\Tests\Unit\Transformer; -use function Flow\ETL\DSL\{array_entry, int_entry, row, rows, str_entry, string_entry}; +use function Flow\ETL\DSL\{int_entry, json_entry, row, rows, str_entry, string_entry}; use Flow\ETL\Transformer\SelectEntriesTransformer; use Flow\ETL\{Config, FlowContext}; use PHPUnit\Framework\TestCase; @@ -17,7 +17,7 @@ public function test_selecting_entries() : void row( int_entry('id', 1), str_entry('name', 'Row Name'), - array_entry('array', ['test']) + json_entry('array', ['test']) ) ); @@ -36,7 +36,7 @@ public function test_selecting_not_existing_entries() : void row( int_entry('id', 1), string_entry('name', 'Row Name'), - array_entry('array', ['test']) + json_entry('array', ['test']) ) ); @@ -53,7 +53,7 @@ public function test_using_select_entries_in_order_to_change_entries_order() : v row( int_entry('id', 1), str_entry('name', 'Row Name'), - array_entry('array', ['test']) + json_entry('array', ['test']) ) ); diff --git a/src/core/etl/tests/Flow/Serializer/Tests/Unit/Base64SerializerTest.php b/src/core/etl/tests/Flow/Serializer/Tests/Unit/Base64SerializerTest.php index 13cf7ba8b..73130ba9b 100644 --- a/src/core/etl/tests/Flow/Serializer/Tests/Unit/Base64SerializerTest.php +++ b/src/core/etl/tests/Flow/Serializer/Tests/Unit/Base64SerializerTest.php @@ -4,7 +4,7 @@ namespace Flow\Serializer\Tests\Unit; -use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, object_entry, row, rows, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; +use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, row, rows, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; use Flow\ETL\{Row, Rows}; use Flow\Serializer\{Base64Serializer, NativePHPSerializer}; use PHPUnit\Framework\TestCase; @@ -22,7 +22,6 @@ public function test_serializing_rows() : void datetime_entry('datetime', new \DateTimeImmutable('2022-01-01 00:00:00')), str_entry('null', null), float_entry('float', 0.12), - object_entry('object', new \ArrayIterator([1, 2, 3])), struct_entry( 'struct', ['integer' => 1, 'string' => 'string'], diff --git a/src/core/etl/tests/Flow/Serializer/Tests/Unit/CompressingSerializerTest.php b/src/core/etl/tests/Flow/Serializer/Tests/Unit/CompressingSerializerTest.php index 2194def77..cc25959f0 100644 --- a/src/core/etl/tests/Flow/Serializer/Tests/Unit/CompressingSerializerTest.php +++ b/src/core/etl/tests/Flow/Serializer/Tests/Unit/CompressingSerializerTest.php @@ -4,7 +4,7 @@ namespace Flow\Serializer\Tests\Unit; -use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, object_entry, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; +use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; use Flow\ETL\{Row, Rows}; use Flow\Serializer\{CompressingSerializer, NativePHPSerializer}; use PHPUnit\Framework\TestCase; @@ -29,7 +29,6 @@ public function test_serializing_rows() : void datetime_entry('datetime', new \DateTimeImmutable('2022-01-01 00:00:00')), str_entry('null', null), float_entry('float', 0.12), - object_entry('object', new \ArrayIterator([1, 2, 3])), struct_entry( 'struct', ['integer' => 1, 'string' => 'string'], diff --git a/src/core/etl/tests/Flow/Serializer/Tests/Unit/NativePHPSerializerTest.php b/src/core/etl/tests/Flow/Serializer/Tests/Unit/NativePHPSerializerTest.php index d3a49900a..aba35d0b1 100644 --- a/src/core/etl/tests/Flow/Serializer/Tests/Unit/NativePHPSerializerTest.php +++ b/src/core/etl/tests/Flow/Serializer/Tests/Unit/NativePHPSerializerTest.php @@ -4,7 +4,7 @@ namespace Flow\Serializer\Tests\Unit; -use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, object_entry, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; +use function Flow\ETL\DSL\{bool_entry, datetime_entry, float_entry, int_entry, str_entry, struct_element, struct_entry, struct_type, type_int, type_string}; use Flow\ETL\{Row, Rows}; use Flow\Serializer\NativePHPSerializer; use PHPUnit\Framework\TestCase; @@ -22,7 +22,6 @@ public function test_serializing_rows() : void datetime_entry('datetime', new \DateTimeImmutable('2022-01-01 00:00:00')), str_entry('null', null), float_entry('float', 0.12), - object_entry('object', new \ArrayIterator([1, 2, 3])), struct_entry( 'struct', ['integer' => 1, 'string' => 'string'], diff --git a/src/lib/filesystem/src/Flow/Filesystem/Partition.php b/src/lib/filesystem/src/Flow/Filesystem/Partition.php index 117e0a7d5..f8f8840ae 100644 --- a/src/lib/filesystem/src/Flow/Filesystem/Partition.php +++ b/src/lib/filesystem/src/Flow/Filesystem/Partition.php @@ -6,12 +6,11 @@ use Flow\ETL\Exception\InvalidArgumentException; use Flow\ETL\Row; -use Flow\ETL\Row\Entry\{ArrayEntry, +use Flow\ETL\Row\Entry\{ DateTimeEntry, JsonEntry, ListEntry, MapEntry, - ObjectEntry, StructureEntry, XMLElementEntry, XMLEntry}; @@ -82,7 +81,7 @@ public static function valueFromRow(Reference $ref, Row $row) : mixed return match ($entry::class) { DateTimeEntry::class => $entry->value()?->format('Y-m-d'), - XMLEntry::class, XMLElementEntry::class, JsonEntry::class, ObjectEntry::class, ListEntry::class, StructureEntry::class, MapEntry::class, ArrayEntry::class => throw new InvalidArgumentException($entry::class . ' can\'t be used as a partition'), + XMLEntry::class, XMLElementEntry::class, JsonEntry::class, ListEntry::class, StructureEntry::class, MapEntry::class => throw new InvalidArgumentException($entry::class . ' can\'t be used as a partition'), default => $entry->toString(), }; }