Skip to content
Open
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
20 changes: 20 additions & 0 deletions src/Schema/Elicitation/ElicitationSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,26 @@ private static function resolveArrayType(array $data): AbstractSchemaDefinition
throw new InvalidArgumentException('Array type must have "items" with either "enum" or "anyOf".');
}

/**
* Return the canonical response content for accepting this elicitation with its declared defaults.
*
* @return array<string, mixed>
*/
public function extractDefaults(): array
{
$defaults = [];

foreach ($this->properties as $name => $property) {
$definition = $property->jsonSerialize();

if (\array_key_exists('default', $definition)) {
$defaults[$name] = $definition['default'];
}
}

return $defaults;
}

/**
* @return array{
* type: string,
Expand Down
15 changes: 10 additions & 5 deletions tests/Conformance/client.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
->setLogger($logger);

/**
* Accepts every elicitation with an empty payload.
* Accepts every form elicitation with its declared defaults.
*
* Enough for the scenarios here, which check that the client asked and echoed
* correctly rather than what a user would have typed.
* ElicitationSchema::extractDefaults() is the canonical accept-with-defaults
* path when the user does not provide explicit field values.
*/
$acceptElicitation = new class($logger) implements RequestHandlerInterface {
public function __construct(private readonly Psr\Log\LoggerInterface $logger)
Expand All @@ -69,9 +69,14 @@ public function supports(Request $request): bool

public function handle(Request $request): Response
{
$this->logger->info('Received elicitation request, accepting with empty content');
if (!$request instanceof ElicitRequest || null === $request->requestedSchema) {
throw new RuntimeException('Expected a form elicitation request with a requested schema.');
}

return new Response($request->getId(), new ElicitResult(ElicitAction::Accept, []));
$content = $request->requestedSchema->extractDefaults();
$this->logger->info(sprintf('Received elicitation request, accepting with %d defaults', count($content)));

return new Response($request->getId(), new ElicitResult(ElicitAction::Accept, $content));
}
};

Expand Down
33 changes: 33 additions & 0 deletions tests/Unit/Schema/Elicitation/ElicitationSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,39 @@ public function testConstructorWithMultipleTypes(): void
$this->assertInstanceOf(EnumSchemaDefinition::class, $schema->properties['rating']);
}

public function testExtractDefaults(): void
{
$schema = ElicitationSchema::fromArray([
'type' => 'object',
'properties' => [
'name' => ['type' => 'string', 'title' => 'Name', 'default' => 'Ada'],
'age' => ['type' => 'integer', 'title' => 'Age', 'default' => 0],
'score' => ['type' => 'number', 'title' => 'Score', 'default' => 95.5],
'color' => ['type' => 'string', 'title' => 'Color', 'enum' => ['red', 'blue'], 'default' => 'blue'],
'plan' => [
'type' => 'string',
'title' => 'Plan',
'oneOf' => [
['const' => 'free', 'title' => 'Free'],
['const' => 'pro', 'title' => 'Pro'],
],
'default' => 'pro',
],
'subscribe' => ['type' => 'boolean', 'title' => 'Subscribe', 'default' => false],
'withoutDefault' => ['type' => 'string', 'title' => 'Without default'],
],
]);

$this->assertSame([
'name' => 'Ada',
'age' => 0,
'score' => 95.5,
'color' => 'blue',
'plan' => 'pro',
'subscribe' => false,
], $schema->extractDefaults());
}

public function testConstructorWithEmptyProperties(): void
{
$this->expectException(InvalidArgumentException::class);
Expand Down