Skip to content

Commit cfd65af

Browse files
authored
Merge pull request #3 from membrane-php/validate-schema-xor-content
Validate parameters contain schema xor content
2 parents 4a455c3 + ce70d9a commit cfd65af

3 files changed

Lines changed: 87 additions & 2 deletions

File tree

src/Exception/InvalidOpenAPI.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public static function duplicateOperationIds(
3030
return new self($message, self::INVALID_OPEN_API);
3131
}
3232

33+
public static function mustHaveSchemaXorContent(string $name): self
34+
{
35+
return new self(
36+
sprintf('Parameter "%s" MUST have either a Schema or Content, but not both.', $name),
37+
self::INVALID_OPEN_API
38+
);
39+
}
40+
3341
public static function failedCebeValidation(string ...$errors): self
3442
{
3543
$message = sprintf("OpenAPI is invalid for the following reasons:\n\t- %s", implode("\n\t- ", $errors));

src/Reader.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,16 @@ private function validate(CebeSpec\OpenApi $openAPI): void
7070
{
7171
$this->isVersionSupported($openAPI->openapi) ?: throw CannotSupport::unsupportedVersion($openAPI->openapi);
7272

73-
$openAPI->validate() ?: throw InvalidOpenAPI::failedCebeValidation(...$openAPI->getErrors());
74-
7573
$existingOperationIds = [];
7674

7775
// OpenAPI Version 3.1 does not require paths
7876
if (isset($openAPI->paths)) {
7977
foreach ($openAPI->paths as $pathUrl => $path) {
78+
$this->parametersContainSchemaXorContent($path);
79+
8080
foreach ($path->getOperations() as $method => $operation) {
81+
$this->parametersContainSchemaXorContent($operation);
82+
8183
Method::tryFrom($method) !== null ?: throw CannotSupport::unsupportedMethod($pathUrl, $method);
8284

8385
isset($operation->operationId) ?: throw CannotSupport::missingOperationId($pathUrl, $method);
@@ -95,10 +97,29 @@ private function validate(CebeSpec\OpenApi $openAPI): void
9597
}
9698
}
9799
}
100+
101+
$openAPI->validate() ?: throw InvalidOpenAPI::failedCebeValidation(...$openAPI->getErrors());
98102
}
99103

100104
private function isVersionSupported(string $version): bool
101105
{
102106
return in_array(OpenAPIVersion::fromString($version), $this->supportedVersions, true);
103107
}
108+
109+
private function parametersContainSchemaXorContent(CebeSpec\PathItem | CebeSpec\Operation $specObject): void
110+
{
111+
foreach ($specObject->parameters as $parameter) {
112+
assert($parameter instanceof CebeSpec\Parameter);
113+
114+
$result = isset($parameter->schema);
115+
116+
if (!empty($parameter->content)) {
117+
$result = !$result;
118+
}
119+
120+
if (!$result) {
121+
throw InvalidOpenAPI::mustHaveSchemaXorContent($parameter->name);
122+
}
123+
}
124+
}
104125
}

tests/ReaderTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,62 @@ public static function provideInvalidOpenAPIs(): Generator
206206
})(),
207207
InvalidOpenAPI::duplicateOperationIds('duplicate-id', '/firstpath', 'get', '/secondpath', 'get'),
208208
];
209+
210+
yield 'path with parameter missing both schema and content' => [
211+
(function () use ($openAPI, $openAPIPath) {
212+
$openAPIArray = $openAPI;
213+
$path = $openAPIPath('get-first-path');
214+
$path['parameters'] = [['name' => 'param', 'in' => 'query']];
215+
$openAPIArray['paths'] = ['/firstpath' => ['get' => $path]];
216+
return json_encode($openAPIArray);
217+
})(),
218+
InvalidOpenAPI::mustHaveSchemaXorContent('param'),
219+
];
220+
221+
yield 'path with parameter both schema and content' => [
222+
(function () use ($openAPI, $openAPIPath) {
223+
$openAPIArray = $openAPI;
224+
$openAPIArray['paths'] = ['/firstpath' => [
225+
'parameters' => [[
226+
'name' => 'param',
227+
'in' => 'query',
228+
'schema' => ['type' => 'string'],
229+
'content' => ['application/json' => ['type' => 'string']]
230+
]],
231+
'get' => $openAPIPath('get-first-path')
232+
],];
233+
return json_encode($openAPIArray);
234+
})(),
235+
InvalidOpenAPI::mustHaveSchemaXorContent('param'),
236+
];
237+
238+
yield 'path with operation missing both schema and content' => [
239+
(function () use ($openAPI, $openAPIPath) {
240+
$openAPIArray = $openAPI;
241+
$openAPIArray['paths'] = ['/firstpath' => [
242+
'parameters' => [['name' => 'param', 'in' => 'query']],
243+
'get' => $openAPIPath('get-first-path')
244+
]];
245+
return json_encode($openAPIArray);
246+
})(),
247+
InvalidOpenAPI::mustHaveSchemaXorContent('param'),
248+
];
249+
250+
yield 'path with operation both schema and content' => [
251+
(function () use ($openAPI, $openAPIPath) {
252+
$openAPIArray = $openAPI;
253+
$path = $openAPIPath('get-first-path');
254+
$path['parameters'] = [[
255+
'name' => 'param',
256+
'in' => 'query',
257+
'schema' => ['type' => 'string'],
258+
'content' => ['application/json' => ['type' => 'string']]
259+
]];
260+
$openAPIArray['paths'] = ['/firstpath' => ['get' => $path],];
261+
return json_encode($openAPIArray);
262+
})(),
263+
InvalidOpenAPI::mustHaveSchemaXorContent('param'),
264+
];
209265
}
210266

211267
#[Test]

0 commit comments

Comments
 (0)