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
19 changes: 10 additions & 9 deletions src/Schema/Elicitation/AbstractSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,37 @@
abstract class AbstractSchemaDefinition implements \JsonSerializable
{
public function __construct(
public readonly string $title,
public readonly ?string $title = null,
public readonly ?string $description = null,
) {
Comment thread
chr-hertel marked this conversation as resolved.
}

/**
* Validate that title exists and is a string in the data array.
* Reject a title that is present but not a string.
*
* @param array<string, mixed> $data
*
* @throws InvalidArgumentException
*/
protected static function validateTitle(array $data, string $schemaType): void
{
if (!isset($data['title']) || !\is_string($data['title'])) {
throw new InvalidArgumentException(\sprintf('Missing or invalid "title" for %s schema definition.', $schemaType));
if (\array_key_exists('title', $data) && null !== $data['title'] && !\is_string($data['title'])) {
throw new InvalidArgumentException(\sprintf('Invalid "title" for %s schema definition.', $schemaType));
}
Comment thread
chr-hertel marked this conversation as resolved.
}

/**
* Build the base JSON structure with type, title, and optional description.
* Build the base JSON structure with type, optional title and description.
*
* @return array<string, mixed>
*/
protected function buildBaseJson(string $type): array
{
$data = [
'type' => $type,
'title' => $this->title,
];
$data = ['type' => $type];

if (null !== $this->title) {
$data['title'] = $this->title;
}

if (null !== $this->description) {
$data['description'] = $this->description;
Expand Down
10 changes: 5 additions & 5 deletions src/Schema/Elicitation/BooleanSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
final class BooleanSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param string|null $description Optional description/help text
* @param bool|null $default Optional default value
*/
public function __construct(
string $title,
?string $title = null,
?string $description = null,
public readonly ?bool $default = null,
) {
Expand All @@ -33,7 +33,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* description?: string,
* default?: bool,
* } $data
Expand All @@ -43,7 +43,7 @@ public static function fromArray(array $data): self
self::validateTitle($data, 'boolean');

return new self(
title: $data['title'],
title: $data['title'] ?? null,
description: $data['description'] ?? null,
default: isset($data['default']) ? (bool) $data['default'] : null,
);
Expand All @@ -52,7 +52,7 @@ public static function fromArray(array $data): self
/**
* @return array{
* type: string,
* title: string,
* title?: string,
* description?: string,
* default?: bool,
* }
Expand Down
22 changes: 12 additions & 10 deletions src/Schema/Elicitation/EnumSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@
final class EnumSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param string[] $enum Array of allowed string values
* @param string|null $description Optional description/help text
* @param string|null $default Optional default value (must be in enum)
* @param string[]|null $enumNames Optional human-readable labels for each enum value
*/
public function __construct(
string $title,
?string $title,
public readonly array $enum,
Comment thread
chr-hertel marked this conversation as resolved.
?string $description = null,
public readonly ?string $default = null,
Expand Down Expand Up @@ -59,7 +59,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* enum: string[],
* description?: string,
* default?: string,
Expand All @@ -75,7 +75,7 @@ public static function fromArray(array $data): self
}

return new self(
title: $data['title'],
title: $data['title'] ?? null,
enum: $data['enum'],
description: $data['description'] ?? null,
default: $data['default'] ?? null,
Expand All @@ -86,7 +86,7 @@ enumNames: $data['enumNames'] ?? null,
/**
* @return array{
* type: string,
* title: string,
* title?: string,
* enum: string[],
* description?: string,
* default?: string,
Expand All @@ -95,11 +95,13 @@ enumNames: $data['enumNames'] ?? null,
*/
public function jsonSerialize(): array
{
$data = [
'type' => 'string',
'title' => $this->title,
'enum' => $this->enum,
];
$data = ['type' => 'string'];

if (null !== $this->title) {
$data['title'] = $this->title;
}

$data['enum'] = $this->enum;

if (null !== $this->description) {
$data['description'] = $this->description;
Expand Down
8 changes: 4 additions & 4 deletions src/Schema/Elicitation/MultiSelectEnumSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
final class MultiSelectEnumSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param string[] $enum Array of allowed string values
* @param string|null $description Optional description/help text
* @param string[]|null $default Optional default selected values (must be subset of enum)
* @param int|null $minItems Optional minimum number of selections
* @param int|null $maxItems Optional maximum number of selections
*/
public function __construct(
string $title,
?string $title,
public readonly array $enum,
Comment on lines 33 to 35

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3546d94 — updated to @param ?string $title Optional human-readable title for the field.

?string $description = null,
public readonly ?array $default = null,
Expand Down Expand Up @@ -73,7 +73,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* items: array{type: string, enum: string[]},
* description?: string,
* default?: string[],
Expand All @@ -90,7 +90,7 @@ public static function fromArray(array $data): self
}

return new self(
title: $data['title'],
title: $data['title'] ?? null,
enum: $data['items']['enum'],
description: $data['description'] ?? null,
default: $data['default'] ?? null,
Expand Down
10 changes: 5 additions & 5 deletions src/Schema/Elicitation/NumberSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
final class NumberSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param bool $integerOnly Whether to restrict to integer values only
* @param string|null $description Optional description/help text
* @param int|float|null $default Optional default value
* @param int|float|null $minimum Optional minimum value (inclusive)
* @param int|float|null $maximum Optional maximum value (inclusive)
*/
public function __construct(
string $title,
?string $title = null,
public readonly bool $integerOnly = false,
Comment on lines 33 to 35

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3546d94 — updated to @param ?string $title Optional human-readable title for the field.

?string $description = null,
public readonly int|float|null $default = null,
Expand Down Expand Up @@ -60,7 +60,7 @@ public function __construct(
/**
* @param array{
* type: string,
* title: string,
* title?: string,
* description?: string,
* default?: int|float,
* minimum?: int|float,
Expand All @@ -75,7 +75,7 @@ public static function fromArray(array $data): self
$integerOnly = 'integer' === $type;

return new self(
title: $data['title'],
title: $data['title'] ?? null,
integerOnly: $integerOnly,
description: $data['description'] ?? null,
default: $data['default'] ?? null,
Expand All @@ -87,7 +87,7 @@ public static function fromArray(array $data): self
/**
* @return array{
* type: string,
* title: string,
* title?: string,
* description?: string,
* default?: int|float,
* minimum?: int|float,
Expand Down
10 changes: 5 additions & 5 deletions src/Schema/Elicitation/StringSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ final class StringSchemaDefinition extends AbstractSchemaDefinition
private const VALID_FORMATS = ['date', 'date-time', 'email', 'uri'];

/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param string|null $description Optional description/help text
* @param string|null $default Optional default value
* @param string|null $format Optional format constraint (date, date-time, email, uri)
* @param int|null $minLength Optional minimum string length
* @param int|null $maxLength Optional maximum string length
*/
public function __construct(
string $title,
?string $title = null,
?string $description = null,
public readonly ?string $default = null,
public readonly ?string $format = null,
Expand Down Expand Up @@ -61,7 +61,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* description?: string,
* default?: string,
* format?: string,
Expand All @@ -74,7 +74,7 @@ public static function fromArray(array $data): self
self::validateTitle($data, 'string');

return new self(
title: $data['title'],
title: $data['title'] ?? null,
description: $data['description'] ?? null,
default: $data['default'] ?? null,
format: $data['format'] ?? null,
Expand All @@ -86,7 +86,7 @@ public static function fromArray(array $data): self
/**
* @return array{
* type: string,
* title: string,
* title?: string,
* description?: string,
* default?: string,
* format?: string,
Expand Down
8 changes: 4 additions & 4 deletions src/Schema/Elicitation/TitledEnumSchemaDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
final class TitledEnumSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param list<array{const: string, title: string}> $oneOf Array of const/title pairs
* @param string|null $description Optional description/help text
* @param string|null $default Optional default value (must match a const)
*/
public function __construct(
string $title,
?string $title,
public readonly array $oneOf,
?string $description = null,
public readonly ?string $default = null,
Expand Down Expand Up @@ -59,7 +59,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* oneOf: list<array{const: string, title: string}>,
* description?: string,
* default?: string,
Expand All @@ -74,7 +74,7 @@ public static function fromArray(array $data): self
}

return new self(
title: $data['title'],
title: $data['title'] ?? null,
oneOf: $data['oneOf'],
description: $data['description'] ?? null,
default: $data['default'] ?? null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
final class TitledMultiSelectEnumSchemaDefinition extends AbstractSchemaDefinition
{
/**
* @param string $title Human-readable title for the field
* @param ?string $title Optional human-readable title for the field
* @param list<array{const: string, title: string}> $anyOf Array of const/title pairs
* @param string|null $description Optional description/help text
* @param string[]|null $default Optional default selected values (must be subset of anyOf consts)
* @param int|null $minItems Optional minimum number of selections
* @param int|null $maxItems Optional maximum number of selections
*/
public function __construct(
string $title,
?string $title,
public readonly array $anyOf,
?string $description = null,
public readonly ?array $default = null,
Expand Down Expand Up @@ -78,7 +78,7 @@ public function __construct(

/**
* @param array{
* title: string,
* title?: string,
* items: array{anyOf: list<array{const: string, title: string}>},
* description?: string,
* default?: string[],
Expand All @@ -95,7 +95,7 @@ public static function fromArray(array $data): self
}

return new self(
title: $data['title'],
title: $data['title'] ?? null,
anyOf: $data['items']['anyOf'],
description: $data['description'] ?? null,
default: $data['default'] ?? null,
Expand Down
16 changes: 13 additions & 3 deletions tests/Unit/Schema/Elicitation/BooleanSchemaDefinitionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,23 @@ public function testFromArrayWithAllParams(): void
$this->assertTrue($schema->default);
}

public function testFromArrayWithMissingTitle(): void
public function testFromArrayWithoutTitleIsAccepted(): void
{
// `title` is optional in the specification, so a server that omits it
// must still be readable.
$schema = BooleanSchemaDefinition::fromArray([]);

$this->assertNull($schema->title);
$this->assertArrayNotHasKey('title', $schema->jsonSerialize());
}

public function testFromArrayRejectsNonStringTitle(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Missing or invalid "title"');
$this->expectExceptionMessage('Invalid "title" for boolean schema definition.');

/* @phpstan-ignore argument.type */
BooleanSchemaDefinition::fromArray([]);
BooleanSchemaDefinition::fromArray(['title' => 42]);
}

public function testJsonSerializeWithMinimalParams(): void
Expand Down
Loading