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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Illuminate/JsonSchema/Types/StringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class StringType extends Type
*/
protected ?string $pattern = null;

/**
* The format of the string.
*/
protected ?string $format = null;

/**
* Set the minimum length (inclusive).
*/
Expand Down Expand Up @@ -49,6 +54,18 @@ public function pattern(string $value): static
return $this;
}

/**
* Set the format of the string.
*
* {@link https://json-schema.org/understanding-json-schema/reference/type#built-in-formats}
*/
public function format(string $value): static
{
$this->format = $value;

return $this;
}

/**
* Set the type's default value.
*/
Expand Down
16 changes: 13 additions & 3 deletions src/Illuminate/JsonSchema/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Illuminate\JsonSchema\Types;

use BackedEnum;
use Illuminate\JsonSchema\JsonSchema;
use Illuminate\JsonSchema\Serializer;
use InvalidArgumentException;

abstract class Type extends JsonSchema
{
Expand Down Expand Up @@ -86,11 +88,19 @@ public function description(string $value): static
/**
* Restrict the value to one of the provided enumerated values.
*
* @param array<int, mixed> $values
* @param class-string<\BackedEnum>|array<int, mixed> $values
*/
public function enum(array $values): static
public function enum(array|string $values): static
{
// Keep order and allow complex values (arrays/objects) without forcing uniqueness...
if (is_string($values)) {
if (! is_subclass_of($values, BackedEnum::class)) {
throw new InvalidArgumentException('The provided class must be a BackedEnum.');
}

$values = array_column($values::cases(), 'value');
}

// Keep order and allow complex values (arrays / objects) without forcing uniqueness...
$this->enum = array_values($values);

return $this;
Expand Down
9 changes: 9 additions & 0 deletions tests/JsonSchema/Fixtures/Enums/IntBackedEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Illuminate\Tests\JsonSchema\Fixtures\Enums;

enum IntBackedEnum: int
{
case One = 1;
case Two = 2;
}
9 changes: 9 additions & 0 deletions tests/JsonSchema/Fixtures/Enums/StringBackedEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Illuminate\Tests\JsonSchema\Fixtures\Enums;

enum StringBackedEnum: string
{
case One = 'one';
case Two = 'two';
}
9 changes: 9 additions & 0 deletions tests/JsonSchema/Fixtures/Enums/UnitEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Illuminate\Tests\JsonSchema\Fixtures\Enums;

enum UnitEnum
{
case One;
case Two;
}
11 changes: 11 additions & 0 deletions tests/JsonSchema/StringTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ public function test_it_sets_pattern()
], $type->toArray());
}

public function test_it_sets_format()
{
$type = (new StringType)->default('foo')->format('date');

$this->assertEquals([
'type' => 'string',
'default' => 'foo',
'format' => 'date',
], $type->toArray());
}

public function test_it_sets_enum()
{
$type = (new StringType)->enum(['draft', 'published']);
Expand Down
36 changes: 36 additions & 0 deletions tests/JsonSchema/TypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
namespace Illuminate\Tests\JsonSchema;

use Illuminate\JsonSchema\JsonSchema;
use Illuminate\Tests\JsonSchema\Fixtures\Enums\IntBackedEnum;
use Illuminate\Tests\JsonSchema\Fixtures\Enums\StringBackedEnum;
use Illuminate\Tests\JsonSchema\Fixtures\Enums\UnitEnum;
use InvalidArgumentException;
use Opis\JsonSchema\Resolvers\SchemaResolver;
use Opis\JsonSchema\SchemaLoader;
use Opis\JsonSchema\Validator;
use Opis\Uri\Uri;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use stdClass;
use Stringable;

class TypeTest extends TestCase
Expand Down Expand Up @@ -102,6 +107,33 @@ public function test_types_in_object_schema(): void
$this->assertInstanceOf(JsonSchema::class, $schema);
}

public function test_throws_with_invalid_enum_string(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The provided class must be a BackedEnum.');
$this->expectExceptionCode(0);

JsonSchema::string()->enum('NonExistentEnumClass');
}

public function test_throws_with_not_an_enum_class(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The provided class must be a BackedEnum.');
$this->expectExceptionCode(0);

JsonSchema::string()->enum(stdClass::class);
}

public function test_throws_with_unit_enum_class(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The provided class must be a BackedEnum.');
$this->expectExceptionCode(0);

JsonSchema::string()->enum(UnitEnum::class);
}

public static function validSchemasProvider(): array
{
return [
Expand All @@ -118,6 +150,7 @@ public static function validSchemasProvider(): array
[JsonSchema::string()->min(1)->max(3), 'a'], // boundary at min
[JsonSchema::string()->pattern('^[A-Z]{2}[0-9]{2}$'), 'AB12'], // complex pattern
[JsonSchema::string()->enum(['', 'x', 'y']), ''], // enum including empty string
[JsonSchema::string()->enum(StringBackedEnum::class), 'one'], // string backed enum cases
[JsonSchema::string()->nullable(), null],
[JsonSchema::string()->nullable(false), ''],

Expand All @@ -132,6 +165,7 @@ public static function validSchemasProvider(): array
[JsonSchema::integer()->max(10), 9], // below max
[JsonSchema::integer()->min(1)->max(3), 3], // boundary at max
[JsonSchema::integer()->enum([0, -1, 5]), 0], // enum with zero
[JsonSchema::integer()->enum(IntBackedEnum::class), 1], // integer backed enum cases
[JsonSchema::integer()->default(0), 0], // default value
[JsonSchema::integer()->nullable(), null],
[JsonSchema::integer()->nullable(false), 0],
Expand Down Expand Up @@ -265,6 +299,7 @@ public static function invalidSchemasProvider(): array
[JsonSchema::string()->max(0), 'a'], // too long for zero max
[JsonSchema::string()->pattern('^[a]+$'), 'ab'], // pattern mismatch
[JsonSchema::string()->enum(['a', 'b']), 'A'], // case sensitive mismatch
[JsonSchema::string()->enum(StringBackedEnum::class), 'three'], // string backed enum cases mismatch
[JsonSchema::string(), null], // null not allowed
[JsonSchema::string()->nullable(false), null], // not nullable

Expand All @@ -279,6 +314,7 @@ public static function invalidSchemasProvider(): array
[JsonSchema::integer()->max(0), 1], // above max boundary
[JsonSchema::integer(), 3.14], // not an integer
[JsonSchema::integer()->enum([1, 2]), 2.5], // not in enum and not an integer
[JsonSchema::integer()->enum(IntBackedEnum::class), 3], // integer backed enum cases mismatch
[JsonSchema::integer()->default(1), null], // wrong type
[JsonSchema::integer()->nullable(false), null], // not nullable

Expand Down