Skip to content

Commit a3db0a4

Browse files
committed
refactor: unify json type and json entry with uuid type and uuid entry
- added Json value to flow-php/types - changed TypeJson from Type<string> into Type<Json> - JsonEntry now holds value as Json - improved static analysis type narrowing
1 parent e686831 commit a3db0a4

File tree

45 files changed

+727
-247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+727
-247
lines changed

documentation/upgrading.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,109 @@ Please follow the instructions for your specific version to ensure a smooth upgr
77

88
---
99

10+
## Upgrading from 0.27.x to 0.28.x
11+
12+
### 1) JsonType now uses Json value object instead of string
13+
14+
The `JsonType` has been refactored to use a dedicated `Json` value object (similar to `Uuid`/`UuidType` pattern).
15+
This allows static analysis tools to distinguish between regular strings and JSON strings.
16+
17+
**Breaking Changes:**
18+
19+
- `JsonType::assert()` now returns `Json` instance instead of `string`
20+
- `JsonType::cast()` now returns `Json` instance instead of `string`
21+
- `JsonType::isValid()` now checks for `Json` instance (plain strings are no longer valid)
22+
- `Cast::cast('json', $value)` function now returns `Json` object instead of string
23+
- `type_json()` return type annotation changed from `Type<string>` to `Type<Json>`
24+
- `JsonEntry::value()` now returns `?Json` instead of `?array` (consistent with `UuidEntry::value()` returning `?Uuid`)
25+
- `JsonEntry::json()` method removed (use `value()` instead)
26+
27+
**Migration:**
28+
29+
If you were using `type_json()->cast($value)` and expected a string, use `->toString()`:
30+
31+
Before:
32+
```php
33+
$jsonString = type_json()->cast($array); // was string
34+
```
35+
36+
After:
37+
```php
38+
$json = type_json()->cast($array); // now Json object
39+
$jsonString = $json->toString(); // get the string
40+
$jsonArray = $json->toArray(); // get as array
41+
```
42+
43+
If you were using `JsonEntry::value()` and edxpected an array:
44+
45+
Before:
46+
```php
47+
$entry = json_entry('data', ['key' => 'value']);
48+
$array = $entry->value(); // was array
49+
```
50+
51+
After:
52+
```php
53+
$entry = json_entry('data', ['key' => 'value']);
54+
$json = $entry->value(); // now Json object
55+
$array = $json?->toArray(); // get as array
56+
$string = $json?->toString(); // get as string
57+
```
58+
59+
If you were using `JsonEntry::json()`:
60+
61+
Before:
62+
```php
63+
$json = $entry->json();
64+
```
65+
66+
After:
67+
```php
68+
$json = $entry->value(); // json() method removed, use value() instead
69+
```
70+
71+
**New Json value object features:**
72+
73+
```php
74+
use Flow\Types\Value\Json;
75+
76+
// Create from string
77+
$json = new Json('{"key": "value"}');
78+
79+
// Create from array
80+
$json = Json::fromArray(['key' => 'value']);
81+
82+
// Check if valid JSON
83+
Json::isValid('{"key": "value"}'); // true
84+
85+
// Convert to string/array
86+
$json->toString(); // '{"key":"value"}'
87+
$json->toArray(); // ['key' => 'value']
88+
89+
// Json implements Stringable
90+
(string) $json; // '{"key":"value"}'
91+
92+
// Json implements JsonSerializable
93+
json_encode($json); // '{"key":"value"}'
94+
```
95+
96+
**Note:** `JsonEntry::value()` now returns `?Json` for consistency with `UuidEntry::value()` returning `?Uuid`. Use `->toArray()` or `->toString()` on the Json object to get the underlying data.
97+
98+
**Row methods behavior:**
99+
100+
```php
101+
// Row::toArray() converts Json to array automatically (for convenient serialization)
102+
$row->toArray(); // Returns ['data' => ['key' => 'value']] not ['data' => Json(...)]
103+
104+
// Row::valueOf() returns the raw value (Json object for json entries)
105+
$row->valueOf('data'); // Returns Json object (use ->toArray() if you need array)
106+
107+
// Entry value() returns the typed value
108+
$row->get('data')->value(); // Returns Json object (use ->toArray() if you need array)
109+
```
110+
111+
---
112+
10113
## Upgrading from 0.26.x to 0.27.x
11114

12115
### 1) Force `EntryFactory $entryFactory` to be required on `array_to_row` & `array_to_row(s)`

src/adapter/etl-adapter-csv/src/Flow/ETL/Adapter/CSV/RowsNormalizer/EntryNormalizer.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Flow\ETL\Adapter\CSV\RowsNormalizer;
66

77
use function Flow\ETL\DSL\date_interval_to_microseconds;
8-
use function Flow\Types\DSL\type_json;
98
use Flow\ETL\Row\Entry;
109
use Flow\ETL\Row\Entry\{DateEntry, DateTimeEntry, EnumEntry, JsonEntry, ListEntry, MapEntry, StructureEntry, TimeEntry, UuidEntry, XMLElementEntry, XMLEntry};
1110

@@ -32,8 +31,8 @@ public function normalize(Entry $entry) : string|float|int|bool|null
3231
EnumEntry::class => $entry->value()?->name,
3332
ListEntry::class,
3433
MapEntry::class,
35-
StructureEntry::class,
36-
JsonEntry::class => type_json()->cast($entry->value()),
34+
StructureEntry::class => \json_encode($entry->value(), \JSON_THROW_ON_ERROR),
35+
JsonEntry::class => $entry->toString(),
3736
default => $entry->value(),
3837
};
3938

src/adapter/etl-adapter-http/tests/Flow/ETL/Adapter/HTTP/Tests/Integration/PsrHttpClientDynamicExtractorTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use function Flow\ETL\DSL\{config, flow_context};
99
use Flow\ETL\Adapter\Http\DynamicExtractor\NextRequestFactory;
1010
use Flow\ETL\Tests\FlowTestCase;
11+
use Flow\Types\Value\Json;
1112
use Http\Mock\Client;
1213
use Nyholm\Psr7\Factory\Psr17Factory;
1314
use Nyholm\Psr7\Response;
@@ -59,8 +60,9 @@ public function create(?ResponseInterface $previousResponse = null) : ?RequestIn
5960
self::assertSame('flow-php', $body['login'], \json_encode($body, JSON_THROW_ON_ERROR));
6061
self::assertSame(73_495_297, $body['id'], \json_encode($body, JSON_THROW_ON_ERROR));
6162

62-
$responseHeaders = $rows->current()->first()->valueOf('response_headers');
63-
\assert(\is_array($responseHeaders));
63+
$responseHeadersValue = $rows->current()->first()->valueOf('response_headers');
64+
self::assertInstanceOf(Json::class, $responseHeadersValue);
65+
$responseHeaders = $responseHeadersValue->toArray();
6466
self::assertSame(['GitHub.com'], $responseHeaders['Server']);
6567
self::assertSame(200, $rows->current()->first()->valueOf('response_status_code'));
6668
self::assertSame('1.1', $rows->current()->first()->valueOf('response_protocol_version'));

src/adapter/etl-adapter-json/src/Flow/ETL/Adapter/JSON/RowsNormalizer/EntryNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function normalize(Entry $entry) : string|float|int|bool|array|null
2929
DateEntry::class => $entry->value()?->format($this->dateFormat),
3030
TimeEntry::class => $entry->value() ? date_interval_to_microseconds($entry->value()) : null,
3131
EnumEntry::class => $entry->value()?->name,
32-
JsonEntry::class => $this->normalizeJsonValue($entry->value()),
32+
JsonEntry::class => $this->normalizeJsonValue($entry->value()?->toArray()),
3333
ListEntry::class,
3434
MapEntry::class,
3535
StructureEntry::class,

src/adapter/etl-adapter-parquet/src/Flow/ETL/Adapter/Parquet/RowsNormalizer.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
namespace Flow\ETL\Adapter\Parquet;
66

77
use function Flow\Types\DSL\type_string;
8-
use Flow\ETL\Row\Entry\{UuidEntry, XMLEntry};
8+
use Flow\ETL\Row\Entry\{JsonEntry, UuidEntry, XMLEntry};
99
use Flow\ETL\{Rows, Schema};
10+
use Flow\Types\Value\Json;
1011

1112
final readonly class RowsNormalizer
1213
{
@@ -35,11 +36,18 @@ public function normalize(Rows $rows, Schema $schema) : array
3536
continue;
3637
}
3738

38-
$columns[$entry->name()] = match ($entry::class) {
39+
$value = match ($entry::class) {
40+
JsonEntry::class => $entry->toString(),
3941
UuidEntry::class => type_string()->cast($entry->value()),
4042
XMLEntry::class => type_string()->cast($entry->value()),
4143
default => $schema->get($entry->ref())->type()->cast($entry->value()),
4244
};
45+
46+
if ($value instanceof Json) {
47+
$value = $value->toString();
48+
}
49+
50+
$columns[$entry->name()] = $value;
4351
}
4452

4553
$normalizedRows[] = $columns;

src/adapter/etl-adapter-parquet/tests/Flow/ETL/Adapter/Parquet/Tests/Integration/ParquetTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ public function test_writing_with_provided_schema() : void
8282

8383
self::assertEquals(
8484
[
85-
['id' => '1', 'name' => 'test', 'uuid' => new \Flow\Types\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]],
86-
['id' => '2', 'name' => 'test', 'uuid' => new \Flow\Types\Value\Uuid('26fd21b0-6080-4d6c-bdb4-1214f1feffef'), 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]],
85+
['id' => '1', 'name' => 'test', 'uuid' => '26fd21b0-6080-4d6c-bdb4-1214f1feffef', 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]],
86+
['id' => '2', 'name' => 'test', 'uuid' => '26fd21b0-6080-4d6c-bdb4-1214f1feffef', 'json' => [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test']]],
8787
],
8888
data_frame($config)
8989
->read(from_parquet($path))

src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer/PHPValueNormalizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace Flow\ETL\Adapter\XML\RowsNormalizer\EntryNormalizer;
66

7-
use function Flow\Types\DSL\{type_json, type_string};
7+
use function Flow\Types\DSL\type_string;
88
use Flow\ETL\Adapter\XML\Abstraction\{XMLAttribute, XMLNode};
99
use Flow\ETL\Exception\InvalidArgumentException;
1010
use Flow\Types\Type;
@@ -114,11 +114,11 @@ public function normalize(string $name, Type $type, mixed $value) : XMLNode|XMLA
114114
IntegerType::class,
115115
BooleanType::class,
116116
FloatType::class => XMLNode::flatNode($name, type_string()->cast($value)),
117-
ArrayType::class => XMLNode::flatNode($name, type_json()->cast($value)),
117+
ArrayType::class => XMLNode::flatNode($name, \is_array($value) ? \json_encode($value, \JSON_THROW_ON_ERROR) : ''),
118118
EnumType::class => XMLNode::flatNode($name, $value instanceof \BackedEnum ? $value->name : ''),
119119
InstanceOfType::class => XMLNode::flatNode($name, type_string()->cast($value)),
120120
DateTimeType::class => XMLNode::flatNode($name, type_string()->cast($value instanceof \DateTimeInterface ? $value->format($this->dateTimeFormat) : '')),
121-
JsonType::class => XMLNode::flatNode($name, type_json()->cast($value)),
121+
JsonType::class => XMLNode::flatNode($name, $value instanceof \Stringable ? $value->__toString() : ''),
122122
UuidType::class => XMLNode::flatNode($name, \is_scalar($value) || $value instanceof \Stringable ? (string) $value : ''),
123123
default => throw new InvalidArgumentException("Given type can't be converted to node, given type: {$type->toString()}"),
124124
};

src/adapter/etl-adapter-xml/tests/Flow/ETL/Adapter/XML/Tests/Unit/RowsNormalizer/EntryNormalizer/PHPValueNormalizerTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Flow\ETL\Adapter\XML\Abstraction\{XMLAttribute, XMLNode};
1919
use Flow\ETL\Adapter\XML\RowsNormalizer\EntryNormalizer\PHPValueNormalizer;
2020
use Flow\ETL\Tests\FlowTestCase;
21+
use Flow\Types\Value\Json;
2122

2223
final class PHPValueNormalizerTest extends FlowTestCase
2324
{
@@ -96,7 +97,7 @@ public function test_normalizing_json_type() : void
9697

9798
self::assertEquals(
9899
XMLNode::flatNode('json', '{"a":"1","b":22}'),
99-
$normalizer->normalize('json', type_json(), ['a' => '1', 'b' => 22])
100+
$normalizer->normalize('json', type_json(), Json::fromArray(['a' => '1', 'b' => 22]))
100101
);
101102
}
102103

0 commit comments

Comments
 (0)