Skip to content

Commit

Permalink
Allow to use Symfony UID for UUID generation
Browse files Browse the repository at this point in the history
  • Loading branch information
stloyd committed Oct 1, 2023
1 parent 9aa0e25 commit fc9c1a7
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 8 deletions.
12 changes: 12 additions & 0 deletions src/core/etl/src/Flow/ETL/DSL/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Flow\ETL\Exception\InvalidArgumentException;
use Flow\ETL\Row\Entries;
use Flow\ETL\Row\Entry as RowEntry;
use Flow\ETL\Row\Entry\Type\Uuid;
use Flow\ETL\Row\Entry\TypedCollection\ScalarType;

/**
Expand Down Expand Up @@ -290,6 +291,14 @@ final public static function structure(string $name, RowEntry ...$entries) : Row
return new RowEntry\StructureEntry($name, ...$entries);
}

/**
* @return RowEntry\UuidEntry
*/
final public static function uuid(string $name, string $value) : RowEntry
{
return new RowEntry\UuidEntry($name, Uuid::fromString($value));
}

/**
* @return RowEntry\XMLEntry
*/
Expand All @@ -298,6 +307,9 @@ final public static function xml(string $name, \DOMDocument|string $data) : RowE
return new RowEntry\XMLEntry($name, $data);
}

/**
* @return RowEntry\XMLNodeEntry
*/
final public static function xml_node(string $name, \DOMNode $data) : RowEntry
{
return new RowEntry\XMLNodeEntry($name, $data);
Expand Down
56 changes: 56 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/Entry/Type/Uuid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types=1);

namespace Flow\ETL\Row\Entry\Type;

use Flow\ETL\Exception\InvalidArgumentException;
use Flow\ETL\Exception\RuntimeException;

final class Uuid
{
/**
* This regexp is a port of the Uuid library,
* which is copyright Ben Ramsey, @see https://github.com/ramsey/uuid.
*/
public const UUID_REGEXP = '/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/ms';

private readonly string $value;

/**
* @throws InvalidArgumentException|RuntimeException
*/
public function __construct(string|\Ramsey\Uuid\UuidInterface|\Symfony\Component\Uid\Uuid $value)
{
if (\is_string($value)) {
try {
if (\class_exists(\Ramsey\Uuid\UuidInterface::class)) {
$this->value = (string) \Ramsey\Uuid\Uuid::fromString($value);
} elseif (\class_exists(\Symfony\Component\Uid\Uuid::class)) {
$this->value = \Symfony\Component\Uid\Uuid::fromString($value)->toRfc4122();
} else {
throw new RuntimeException("\Ramsey\Uuid\Uuid nor \Symfony\Component\Uid\Uuid class not found, please add 'ramsey/uuid' or 'symfony/uid' as a dependency to the project first.");
}
} catch (\InvalidArgumentException $e) {
throw new InvalidArgumentException("Invalid UUID: '{$value}'", 0, $e);
}
} elseif ($value instanceof \Ramsey\Uuid\UuidInterface) {
$this->value = $value->toString();
} else {
$this->value = $value->toRfc4122();
}
}

public static function fromString(string $value) : self
{
return new self($value);
}

public function isEqual(self $type) : bool
{
return $this->toString() === $type->toString();
}

public function toString() : string
{
return $this->value;
}
}
96 changes: 96 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/Entry/UuidEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

namespace Flow\ETL\Row\Entry;

use Flow\ETL\Exception\InvalidArgumentException;
use Flow\ETL\Row\Entry;
use Flow\ETL\Row\Reference;
use Flow\ETL\Row\Schema\Definition;

/**
* @implements Entry<Entry\Type\Uuid, array{name: string, value: string}>
*/
final class UuidEntry implements \Stringable, Entry
{
use EntryRef;

/**
* @throws InvalidArgumentException
*/
public function __construct(private readonly string $name, private readonly Entry\Type\Uuid $value)
{
if ('' === $name) {
throw InvalidArgumentException::because('Entry name cannot be empty');
}
}

public static function from(string $name, string $value) : self
{
return new self($name, Entry\Type\Uuid::fromString($value));
}

public function __serialize() : array
{
return ['name' => $this->name, 'value' => $this->value->toString()];
}

public function __toString() : string
{
return $this->toString();
}

public function __unserialize(array $data) : void
{
$this->name = $data['name'];
$this->value = new Entry\Type\Uuid($data['value']);
}

public function definition() : Definition
{
return Definition::uuid($this->name);
}

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->value()->isEqual($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
{
return $this->value->toString();
}

public function value() : Entry\Type\Uuid
{
return $this->value;
}
}
21 changes: 21 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/Factory/NativeEntryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public function create(string $entryName, mixed $value) : Entry
return Row\Entry\JsonEntry::fromJsonString($entryName, $value);
}

if ($this->isUuid($value)) {
return new Row\Entry\UuidEntry($entryName, Entry\Type\Uuid::fromString($value));
}

if ($this->isXML($value)) {
return new Entry\XMLEntry($entryName, $value);
}
Expand Down Expand Up @@ -89,6 +93,14 @@ public function create(string $entryName, mixed $value) : Entry
return new Row\Entry\DateTimeEntry($entryName, $value);
}

if ($value instanceof Entry\Type\Uuid || $value instanceof \Ramsey\Uuid\UuidInterface || $value instanceof \Symfony\Component\Uid\Uuid) {
if ($value instanceof \Ramsey\Uuid\UuidInterface || $value instanceof \Symfony\Component\Uid\Uuid) {
return new Row\Entry\UuidEntry($entryName, new Entry\Type\Uuid($value));
}

return new Row\Entry\UuidEntry($entryName, $value);
}

return new Row\Entry\ObjectEntry($entryName, $value);
}

Expand Down Expand Up @@ -195,6 +207,10 @@ private function fromDefinition(Schema\Definition $definition, mixed $value) : E
return EntryDSL::xml($definition->entry()->name(), $value);
}

if ($type === Entry\UuidEntry::class && (\is_string($value) || $value instanceof Entry\Type\Uuid)) {
return EntryDSL::uuid($definition->entry()->name(), \is_string($value) ? $value : $value->toString());
}

if ($type === Entry\ObjectEntry::class && \is_object($value)) {
return EntryDSL::object($definition->entry()->name(), $value);
}
Expand Down Expand Up @@ -289,6 +305,11 @@ private function isJson(string $string) : bool
}
}

private function isUuid(string $string) : bool
{
return 0 !== \preg_match(Entry\Type\Uuid::UUID_REGEXP, $string);
}

private function isXML(string $string) : bool
{
try {
Expand Down
29 changes: 24 additions & 5 deletions src/core/etl/src/Flow/ETL/Row/Reference/Expression/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
use Flow\ETL\Row;
use Flow\ETL\Row\Reference\Expression;

if (!\class_exists(\Ramsey\Uuid\Uuid::class)) {
throw new RuntimeException("\Ramsey\Uuid\Uuid class not found, please add ramsey/uuid dependency to the project first.");
if (!\class_exists(\Ramsey\Uuid\Uuid::class) && !\class_exists(\Symfony\Component\Uid\Uuid::class)) {
throw new RuntimeException("\Ramsey\Uuid\Uuid nor \Symfony\Component\Uid\Uuid class not found, please add 'ramsey/uuid' or 'symfony/uid' as a dependency to the project first.");
}

final class Uuid implements Expression
{
private function __construct(private readonly string $uuidVersion, private readonly ?Expression $ref = null)
Expand All @@ -33,9 +34,27 @@ public function eval(Row $row) : mixed
$param = $this->ref?->eval($row);

return match ($this->uuidVersion) {
'uuid4' => \Ramsey\Uuid\Uuid::uuid4(),
'uuid7' => $param instanceof \DateTimeInterface?\Ramsey\Uuid\Uuid::uuid7($param):null,
default=> null
'uuid4' => $this->generateV4(),
'uuid7' => $param instanceof \DateTimeInterface ? $this->generateV7($param) : null,
default => null
};
}

private function generateV4() : \Symfony\Component\Uid\UuidV4|\Ramsey\Uuid\UuidInterface
{
if (\class_exists(\Ramsey\Uuid\Uuid::class)) {
return \Ramsey\Uuid\Uuid::uuid4();
}

return \Symfony\Component\Uid\UuidV4::v4();
}

private function generateV7(\DateTimeInterface $dateTime) : \Symfony\Component\Uid\UuidV7|\Ramsey\Uuid\UuidInterface
{
if (\class_exists(\Ramsey\Uuid\Uuid::class)) {
return \Ramsey\Uuid\Uuid::uuid7($dateTime);
}

return new \Symfony\Component\Uid\UuidV7(\Symfony\Component\Uid\UuidV7::generate($dateTime));
}
}
6 changes: 6 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/Schema/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Flow\ETL\Row\Entry\StringEntry;
use Flow\ETL\Row\Entry\StructureEntry;
use Flow\ETL\Row\Entry\TypedCollection\Type;
use Flow\ETL\Row\Entry\UuidEntry;
use Flow\ETL\Row\Entry\XMLEntry;
use Flow\ETL\Row\EntryReference;
use Flow\ETL\Row\Schema\Constraint\Any;
Expand Down Expand Up @@ -163,6 +164,11 @@ public static function union(string|EntryReference $entry, array $entryClasses,
return new self($entry, $types, $constraint, $metadata);
}

public static function uuid(string|EntryReference $entry, ?Constraint $constraint = null, ?Metadata $metadata = null) : self
{
return new self($entry, [UuidEntry::class], $constraint, $metadata);
}

public static function xml(string|EntryReference $entry, bool $nullable = false, ?Constraint $constraint = null, ?Metadata $metadata = null) : self
{
return new self($entry, ($nullable) ? [XMLEntry::class, NullEntry::class] : [XMLEntry::class], $constraint, $metadata);
Expand Down
Loading

0 comments on commit fc9c1a7

Please sign in to comment.