Skip to content

Commit

Permalink
Improve validation and casting of request parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
bastien-phi committed Mar 14, 2024
1 parent a1b8247 commit a9de400
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
47 changes: 28 additions & 19 deletions src/Validation/RequestValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,25 +107,9 @@ protected function validateParameters(): void
}

if ($parameterValue) {
if (isset($expectedParameterSchema->type) && gettype($parameterValue) !== $expectedParameterSchema->type) {
$expectedType = $expectedParameterSchema->type;

$expectedType = match ($expectedType) {
'integer' => 'int',
'number' => 'float',
default => $expectedType,
};

if (is_numeric($parameterValue)) {
$parameterValue = match ($expectedType) {
'int' => (int) $parameterValue,
'float' => (float) $parameterValue,
default => $parameterValue,
};
}
}
$parameterValue = $this->castParameterValue($parameterValue, $expectedParameterSchema);

$result = $validator->validate($parameterValue, $expectedParameterSchema);
$result = $validator->validate($this->toObject($parameterValue), $expectedParameterSchema);

// If the result is not valid, then display failure reason.
if ($result->isValid() === false) {
Expand Down Expand Up @@ -213,8 +197,33 @@ protected function parseBodySchema(): stdClass
return $this->toObject($body);
}

private function castParameterValue(mixed $parameterValue, ?stdClass $expectedSchema): mixed
{
if ($expectedSchema === null || ! isset($expectedSchema->type)) {
return $parameterValue;
}

if ($expectedSchema->type === 'integer' && is_numeric($parameterValue)) {
return (int) $parameterValue;
} elseif ($expectedSchema->type === 'number' && is_numeric($parameterValue)) {
return (float) $parameterValue;
} elseif ($expectedSchema->type === 'object' && Arr::isAssoc($parameterValue)) {
return Arr::map(
$parameterValue,
fn (mixed $value, string $key) => $this->castParameterValue($value, $expectedSchema->properties?->{$key})
);
} elseif ($expectedSchema->type === 'array' && is_array($parameterValue)) {
return Arr::map(
$parameterValue,
fn (mixed $value) => $this->castParameterValue($value, $expectedSchema->items)
);
}

return $parameterValue;
}

/**
* @return ($data is array<string, mixed> ? object : ($data is array<int, mixed> ? array<int, mixed> : mixed))
* @return ($data is array<string, mixed> ? \stdClass : ($data is array<int, mixed> ? array<int, mixed> : mixed))
*/
private function toObject(mixed $data): mixed
{
Expand Down
22 changes: 22 additions & 0 deletions tests/Fixtures/Arrays.v1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,27 @@ paths:
responses:
'204':
description: No Content
/parameter-as-array-of-objects:
parameters:
- name: arrayParam
in: query
schema:
type: array
items:
type: object
properties:
id:
type: integer
name:
type: string
required:
- id
- name
get:
summary: Get arrays of objects
tags: [ ]
responses:
'204':
description: No Content
components:
schemas: {}
16 changes: 16 additions & 0 deletions tests/RequestValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,22 @@ public function test_handles_array_query_parameters(): void
->assertValidRequest();
}

public function test_handles_array_of_object_query_parameters(): void
{
Spectator::using('Arrays.v1.yml');

Route::get('/parameter-as-array-of-objects', function () {
return response()->noContent();
})->middleware(Middleware::class);

$this->get('/parameter-as-array-of-objects?arrayParam[0][id]=1&arrayParam[0][name]=foo&arrayParam[1][id]=2')
->assertValidationMessage('The required properties (name) are missing')
->assertInvalidRequest();

$this->get('/parameter-as-array-of-objects?arrayParam[0][id]=1&arrayParam[0][name]=foo&arrayParam[1][id]=2&arrayParam[1][name]=bar')
->assertValidRequest();
}

public function test_handles_query_parameters_int(): void
{
Spectator::using('Test.v1.json');
Expand Down

0 comments on commit a9de400

Please sign in to comment.