Skip to content

Commit 869c84a

Browse files
authored
Merge pull request #21 from membrane-php/without-servers
Add withoutServer method to OpenAPI object
2 parents f054278 + c2c26b8 commit 869c84a

File tree

11 files changed

+566
-231
lines changed

11 files changed

+566
-231
lines changed

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
"cebe/php-openapi": "^1.7"
2020
},
2121
"require-dev": {
22-
"phpunit/phpunit": "^10.1",
23-
"phpstan/phpstan": "^1.10.56",
24-
"squizlabs/php_codesniffer": "^3.7",
22+
"phpunit/phpunit": "^10.5.36",
23+
"phpstan/phpstan": "^1.12.6",
24+
"squizlabs/php_codesniffer": "^3.5.4",
2525
"mikey179/vfsstream": "^1.6.7",
26-
"infection/infection": "^0.27.0"
26+
"infection/infection": "^0.29.7"
2727
},
2828
"config": {
2929
"allow-plugins": {

src/Factory/V30/FromCebe.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ public static function createOpenAPI(
2626

2727
/**
2828
* todo when phpstan 1.11 stable is released
29-
* replace the below lines with @phpstan-ignore nullsafe.neverNull
29+
* replace the below lines with phpstan-ignore nullsafe.neverNull
3030
* The reason for this is the cebe library does not specify that info is nullable
3131
* However it is not always set, so it can be null
3232
*/
33-
return new Valid\V30\OpenAPI(new OpenAPI(
33+
return Valid\V30\OpenAPI::fromPartial(new OpenAPI(
3434
$openApi->openapi,
3535
$openApi->info?->title, // @phpstan-ignore-line
3636
$openApi->info?->version, // @phpstan-ignore-line

src/ValueObject/Valid/Identifier.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@ public function __construct(string $field, string ...$fields)
1616
$this->chain = [$field, ...$fields];
1717
}
1818

19-
public function append(string $primaryId, string $secondaryId = ''): self
19+
public function append(string $primaryId, string $secondaryId = ''): Identifier
2020
{
2121
$field = sprintf(
2222
'%s%s',
2323
$primaryId,
2424
$secondaryId === '' ? '' : "($secondaryId)"
2525
);
2626

27-
return new self(...[...$this->chain, $field]);
27+
return new Identifier(...[...$this->chain, $field]);
2828
}
2929

30-
public function fromEnd(int $level): ?string
30+
public function fromStart(int $level = 0): ?string
31+
{
32+
return $this->chain[$level] ?? null;
33+
}
34+
35+
public function fromEnd(int $level = 0): ?string
3136
{
3237
return array_reverse($this->chain)[$level] ?? null;
3338
}

src/ValueObject/Valid/V30/OpenAPI.php

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,88 +13,99 @@
1313
final class OpenAPI extends Validated
1414
{
1515
/**
16+
* @param array<int, Server> $servers
1617
* Optional, may be left empty.
1718
* If empty or unspecified, the array will contain the default Server.
1819
* The default Server has "url" === "/" and no "variables"
19-
* @var array<int, Server>
20-
*/
21-
public readonly array $servers;
22-
23-
/**
24-
* REQUIRED
20+
*
21+
* @param array<string,PathItem> $paths
22+
* REQUIRED:
2523
* It may be empty due to ACL constraints
2624
* The PathItem's relative endpoint key mapped to the PathItem
27-
* @var array<string,PathItem>
2825
*/
29-
public readonly array $paths;
26+
private function __construct(
27+
Identifier $identifier,
28+
public readonly array $servers,
29+
public readonly array $paths
30+
) {
31+
parent::__construct($identifier);
3032

31-
public function __construct(Partial\OpenAPI $openAPI)
33+
$this->reviewServers($this->servers);
34+
$this->reviewPaths($this->paths);
35+
}
36+
37+
public function withoutServers(): OpenAPI
3238
{
33-
if (!isset($openAPI->title) || !isset($openAPI->version)) {
34-
throw InvalidOpenAPI::missingInfo();
35-
}
39+
return new OpenAPI(
40+
$this->getIdentifier(),
41+
[new Server($this->getIdentifier(), new Partial\Server('/'))],
42+
array_map(fn($p) => $p->withoutServers(), $this->paths),
43+
);
44+
}
3645

37-
parent::__construct(new Identifier("$openAPI->title($openAPI->version)"));
46+
public static function fromPartial(Partial\OpenAPI $openAPI): self
47+
{
48+
$identifier = new Identifier(sprintf(
49+
'%s(%s)',
50+
$openAPI->title ?? throw InvalidOpenAPI::missingInfo(),
51+
$openAPI->version ?? throw InvalidOpenAPI::missingInfo(),
52+
));
3853

39-
if (!isset($openAPI->openAPI)) {
40-
throw InvalidOpenAPI::missingOpenAPIVersion($this->getIdentifier());
41-
}
54+
$openAPI->openAPI ??
55+
throw InvalidOpenAPI::missingOpenAPIVersion($identifier);
4256

43-
$this->servers = $this->validateServers(
44-
$this->getIdentifier(),
45-
$openAPI->servers
46-
);
57+
$servers = self::validateServers($identifier, $openAPI->servers);
58+
$paths = self::validatePaths($identifier, $servers, $openAPI->paths);
4759

48-
$this->paths = $this->validatePaths(
49-
$this->getIdentifier(),
50-
$openAPI->paths,
51-
);
60+
return new OpenAPI($identifier, $servers, $paths);
5261
}
5362

5463
/**
5564
* @param Partial\Server[] $servers
5665
* @return array<int,Server>
5766
*/
58-
private function validateServers(
67+
private static function validateServers(
5968
Identifier $identifier,
6069
array $servers
6170
): array {
6271
if (empty($servers)) {
6372
return [new Server($identifier, new Partial\Server('/'))];
6473
}
6574

66-
$result = array_values(array_map(
75+
return array_values(array_map(
6776
fn($s) => new Server($identifier, $s),
6877
$servers
6978
));
79+
}
7080

71-
$uniqueURLS = array_unique(array_map(fn($s) => $s->url, $result));
72-
if (count($result) !== count($uniqueURLS)) {
81+
/**
82+
* @param Server[] $servers
83+
*/
84+
private function reviewServers(array $servers): void
85+
{
86+
$uniqueURLS = array_unique(array_map(fn($s) => $s->url, $servers));
87+
if (count($servers) !== count($uniqueURLS)) {
7388
$this->addWarning(
7489
'Server URLs are not unique',
7590
Warning::IDENTICAL_SERVER_URLS
7691
);
7792
}
78-
79-
return $result;
8093
}
8194

82-
/**
95+
/**
96+
* @param Server[] $servers
8397
* @param null|Partial\PathItem[] $pathItems
8498
* @return array<string,PathItem>
8599
*/
86-
private function validatePaths(
100+
private static function validatePaths(
87101
Identifier $identifier,
102+
array $servers,
88103
?array $pathItems
89104
): array {
90105
if (is_null($pathItems)) {
91106
throw InvalidOpenAPI::missingPaths($identifier);
92107
}
93108

94-
if (empty($pathItems)) {
95-
$this->addWarning('No Paths in OpenAPI', Warning::EMPTY_PATHS);
96-
}
97-
98109
$result = [];
99110

100111
foreach ($pathItems as $pathItem) {
@@ -115,27 +126,37 @@ private function validatePaths(
115126
);
116127
}
117128

118-
$result[$pathItem->path] = new PathItem(
129+
$result[$pathItem->path] = PathItem::fromPartial(
119130
$identifier->append($pathItem->path),
120-
$this->servers,
131+
$servers,
121132
$pathItem
122133
);
123134
}
124135

125-
$this->checkForEquivalentPathTemplates($result);
126-
$this->checkForDuplicatedOperationIds($result);
136+
self::checkForEquivalentPathTemplates($result);
137+
self::checkForDuplicatedOperationIds($result);
127138

128139
return $result;
129140
}
130141

142+
/**
143+
* @param PathItem[] $paths
144+
*/
145+
private function reviewPaths(array $paths): void
146+
{
147+
if (empty($paths)) {
148+
$this->addWarning('No Paths in OpenAPI', Warning::EMPTY_PATHS);
149+
}
150+
}
151+
131152
/**
132153
* @param array<string,PathItem> $pathItems
133154
*/
134-
private function checkForEquivalentPathTemplates(array $pathItems): void
155+
private static function checkForEquivalentPathTemplates(array $pathItems): void
135156
{
136157
$regexToIdentifier = [];
137158
foreach ($pathItems as $path => $pathItem) {
138-
$regex = $this->getPathRegex($path);
159+
$regex = self::getPathRegex($path);
139160

140161
if (isset($regexToIdentifier[$regex])) {
141162
throw InvalidOpenAPI::equivalentTemplates(
@@ -151,7 +172,7 @@ private function checkForEquivalentPathTemplates(array $pathItems): void
151172
/**
152173
* @param PathItem[] $paths
153174
*/
154-
private function checkForDuplicatedOperationIds(array $paths): void
175+
private static function checkForDuplicatedOperationIds(array $paths): void
155176
{
156177
$checked = [];
157178

@@ -174,7 +195,7 @@ private function checkForDuplicatedOperationIds(array $paths): void
174195
}
175196
}
176197

177-
private function getPathRegex(string $path): string
198+
private static function getPathRegex(string $path): string
178199
{
179200
$pattern = preg_replace('#{[^/]+}#', '{([^/]+)}', $path);
180201

0 commit comments

Comments
 (0)