Skip to content

Commit 4b5863c

Browse files
authored
Merge pull request #42 from membrane-php/add-3.1-parity
Add 3.1 parity
2 parents dd32e00 + 710d062 commit 4b5863c

54 files changed

Lines changed: 9449 additions & 581 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"require": {
1818
"php": "^8.1.0",
19+
"symfony/yaml": "^4 || ^5 || ^6 || ^7",
1920
"devizzent/cebe-php-openapi": "^1.1.2"
2021
},
2122
"require-dev": {

docs/object-simplifications.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# How The Reader Simplifies Your OpenAPI
2+
3+
Besides simply parsing your OpenAPI into `Validated` objects,
4+
the reader is designed to _simplify the developer experience_ of other OpenAPI tools.
5+
6+
## Standard Simplifications
7+
8+
### Never Worry About Uninitialized Properties
9+
Properties should be safe to access:
10+
All properties have a value.
11+
`null` represents an omitted field _if_ no other value can be safely assumed.
12+
13+
### Strong Typehints
14+
Data structures should be easily discoverable.
15+
All properties should have strong typehints.
16+
17+
## Opinionated Simplifications
18+
19+
### Narrow Schemas To False If Impossible To Pass
20+
The `false` boolean schema explicitly states any input will fail.
21+
The Reader will narrow schemas to `false` if it is proven impossible to pass.
22+
This optimizes code that may otherwise validate input that will always fail.
23+
24+
#### Enum Specified Empty
25+
Any schema specifying an empty `enum`, is narrowed to the `false` boolean schema.
26+
27+
If `enum` is specified, it contains the exhaustive list of valid values.
28+
If `enum` is specified as an empty array, there are no valid values.
29+
30+
#### Enum Without A Valid Value
31+
If a schema specifies `enum` without a value that passes the rest of the schema;
32+
it is impossible to pass, it will be narrowed to the `false` boolean schema.
33+
34+
### Narrow Typehints
35+
Typehints are narrowed if it has no impact on expressiveness.
36+
37+
#### AllOf, AnyOf and OneOf Are Always Arrays
38+
39+
`allOf`, `anyOf` and `oneOf` can express two things:
40+
1. There are subschemas
41+
2. There are not
42+
43+
To express there are no subschemas, the value is omitted.
44+
45+
As such, the Reader structures `allOf`, `anyOf` and `oneOf` in two ways:
46+
1. A non-empty array
47+
2. An empty array
48+
49+
Though these keywords are not allowed to be empty,
50+
[The Reader allows it](validation-deviations.md#allof-anyof-and-oneof-can-be-empty)
51+
for the sake of simplicity.
52+
53+
This simplifies code that loops through subschemas.
54+
55+
#### String Metadata Is Always String
56+
57+
Optional metadata is expressed in two ways:
58+
1. There is data
59+
2. There is not
60+
61+
As such, the Reader structures metadata in two ways:
62+
1. A string containing non-whitespace characters
63+
2. An empty string `''`
64+
65+
When accessing string metadata only one check is necessary:
66+
67+
```php
68+
if ($metadata !== '') {
69+
// do something...
70+
}
71+
```
72+
73+
### Combined Fields
74+
Data is combined, if and only if, it has no impact on expressiveness.
75+
76+
#### Maximum|Minimum are combined with ExclusiveMaximum|ExclusiveMinimum
77+
78+
[//]: # (TODO explain how they are combined for 3.0 and in 3.1 we take the more restrictive keyword)
79+
80+
A numerical limit can only be expressed in three ways:
81+
- There is no limit
82+
- There is an inclusive limit
83+
- There is an exclusive limit
84+
85+
As such the Reader combines the relevant keywords into:
86+
- `Limit|null $maximum`.
87+
- `Limit|null $minimum`.
88+
89+
Where `Limit` has two properties:
90+
- `float|int $limit`
91+
- `bool $exclusive`
92+
93+
#### Const Overrides Enum in 3.1
94+
95+
[//]: # (TODO Flesh out a bit)
96+
97+
The more restrictive keyword takes precedence.

docs/validation-deviations.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Validation And How It Deviates From OpenAPI Specification
2+
This page documents where validation deviates from the OpenAPI Specification.
3+
4+
## Stricter Requirements
5+
It is stricter when it comes to providing an unambiguous specification.
6+
This helps to avoid confusion as well as simplify development for other OpenAPI tools.
7+
8+
### OperationId Is Required
9+
10+
Operations MUST have an [operationId](https://github.com/OAI/OpenAPI-Specification/blob/3.1.0/versions/3.0.3.md#fixed-fields-8).
11+
12+
`operationId` is a unique string used to identify an operation.
13+
By making it required, it serves as a _reliable_ method of identification.
14+
15+
### Query Strings Must Be Unambiguous
16+
17+
Operations MUST NOT use more than one _ambiguous parameter_.
18+
19+
In this context, an _ambiguous parameter_ is defined as being `in:query` with one of the following combinations:
20+
- `type:object` with `style:form` and `explode:true`
21+
- `type:object` or `type:array` with `style:spaceDelimited`
22+
- `type:object` or `type:array` with `style:pipeDelimited`
23+
24+
If everything else can be identified; then through a process of elimination, the ambiguous segment belongs to the _ambiguous parameter_
25+
26+
If an operation contains two or more _ambiguous parameters_, then there are multiple ways of interpreting the ambiguous segment.
27+
This ambiguity means the query string cannot be resolved deterministically.
28+
As such, it is not allowed.
29+
30+
## Looser Requirements
31+
32+
These requirements are looser than the OpenAPI Specification.
33+
Where the OpenAPI Specification would be invalid, the reader will add a warning to the `Validated` object.
34+
35+
### MultipleOf Can Be Negative
36+
37+
Normally `multipleOf` MUST be a positive non-zero number.
38+
39+
The Reader allows `multipleOf` to be any non-zero number.
40+
A multiple of a negative number is also a multiple of its absolute value.
41+
It's more confusing, but what is expressed is identical.
42+
43+
Therefore, if a negative value is given:
44+
- You will receive a Warning
45+
- The absolute value will be used
46+
47+
### AllOf, AnyOf and OneOf Can Be Empty
48+
49+
Normally, `allOf`, `anyOf` and `oneOf` MUST be non-empty.
50+
51+
The Reader allows them to be empty.
52+
If any of these keywords are omitted, they are treated as empty arrays.
53+
54+
55+
Therefore, if an empty array is given:
56+
- You will receive a Warning
57+
- An empty array will be used.
58+
59+
If it is omitted:
60+
- An empty array will be used.
61+
62+
[This is done for simplicity](object-simplifications.md#allof-anyof-and-oneof-are-always-arrays).
63+
64+
### Required Can Be Empty
65+
66+
OpenAPI 3.0 states `required` MUST be non-empty.
67+
OpenAPI 3.1 allows `required` to be empty and defaults to empty if omitted.
68+
69+
The Reader always allows `required` to be empty and defaults to empty if omitted.
70+
This allows us to narrow the typehint for `required` to always being an array.
71+
72+
If an empty array is given:
73+
- If your API is using 3.0, you will receive a Warning
74+
- An empty array will be used
75+
76+
### Required Can Contain Duplicates
77+
78+
Normally `required` MUST contain unique values.
79+
80+
The Reader allows `required` to contain duplicates.
81+
82+
If a duplicate item is found:
83+
- You will receive a Warning
84+
- The duplicate will be removed.

docs/validation.md

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/Exception/InvalidOpenAPI.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Membrane\OpenAPIReader\Exception;
66

7+
use Membrane\OpenAPIReader\ValueObject\Valid\Enum\Type;
78
use Membrane\OpenAPIReader\ValueObject\Valid\Identifier;
89
use RuntimeException;
910

@@ -312,11 +313,14 @@ public static function invalidType(Identifier $identifier, string $type): self
312313
return new self($message);
313314
}
314315

315-
public static function typeArrayInWrongVersion(Identifier $identifier): self
316-
{
316+
public static function keywordMustBeType(
317+
Identifier $identifier,
318+
string $keyword,
319+
Type $type,
320+
): self {
317321
$message = <<<TEXT
318322
$identifier
319-
Specifying type as an array is only valid for OpenAPI ^3.1
323+
$keyword MUST be $type->value
320324
TEXT;
321325

322326
return new self($message);
@@ -356,19 +360,19 @@ public static function boolExclusiveMinMaxIn31(
356360
return new self($message);
357361
}
358362

359-
public static function keywordMustBeStrictlyPositiveNumber(
363+
public static function keywordCannotBeZero(
360364
Identifier $identifier,
361365
string $keyword,
362366
): self {
363367
$message = <<<TEXT
364368
$identifier
365-
$keyword MUST be strictly greater than zero
369+
$keyword MUST not be zero
366370
TEXT;
367371

368372
return new self($message);
369373
}
370374

371-
public static function keywordMustBeNegativeInteger(
375+
public static function keywordMustBeNonNegativeInteger(
372376
Identifier $identifier,
373377
string $keyword,
374378
): self {
@@ -445,4 +449,13 @@ public static function responseCodeMustBeNumericOrDefault(
445449

446450
return new self($message);
447451
}
452+
453+
public static function defaultMustConformToType(
454+
Identifier $identifier,
455+
): self {
456+
return new self(<<<TEXT
457+
$identifier
458+
- default MUST conform to type
459+
TEXT);
460+
}
448461
}

src/Factory/V30/FromCebe.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private static function createParameters(array $parameters): array
134134

135135
private static function createSchema(
136136
Cebe\Reference|Cebe\Schema|null $schema
137-
): ?Schema {
137+
): Schema|null {
138138
assert(!$schema instanceof Cebe\Reference);
139139

140140
if ($schema === null) {

0 commit comments

Comments
 (0)