1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Swaggest\ApiCompat; |
4
|
|
|
|
5
|
|
|
use Swaggest\JsonDiff\JsonDiff; |
6
|
|
|
use Swaggest\JsonDiff\JsonPointer; |
7
|
|
|
|
8
|
|
|
class ApiCompat |
9
|
|
|
{ |
10
|
|
|
private $original; |
11
|
|
|
private $new; |
12
|
|
|
|
13
|
|
|
/** @var JsonDiff */ |
14
|
|
|
private $jsonDiff; |
15
|
|
|
|
16
|
|
|
/** @var Change[] */ |
17
|
|
|
private $breakingChanges = array(); |
18
|
|
|
|
19
|
1 |
|
public function __construct($original, $new) |
20
|
|
|
{ |
21
|
1 |
|
$this->jsonDiff = new JsonDiff($original, $new, JsonDiff::JSON_URI_FRAGMENT_ID + JsonDiff::REARRANGE_ARRAYS); |
22
|
|
|
|
23
|
1 |
|
$this->original = $original; |
24
|
1 |
|
$this->new = $this->jsonDiff->getRearranged(); |
25
|
|
|
|
26
|
1 |
|
$this->checkModifications(); |
27
|
1 |
|
$this->checkAdditions(); |
28
|
1 |
|
$this->checkRemovals(); |
29
|
1 |
|
} |
30
|
|
|
|
31
|
1 |
|
public function getBreakingChanges() |
32
|
|
|
{ |
33
|
1 |
|
return $this->breakingChanges; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
public function getDiff() |
37
|
|
|
{ |
38
|
|
|
return $this->jsonDiff; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
|
42
|
1 |
|
private function checkAdditions() |
43
|
|
|
{ |
44
|
1 |
|
foreach ($this->jsonDiff->getAddedPaths() as $path) { |
45
|
1 |
|
$new = JsonPointer::getByPointer($this->new, $path); |
46
|
|
|
switch (true) { |
47
|
1 |
|
case Path::fitsPattern($path, '#/definitions/*/required'): |
48
|
1 |
|
$this->breakingChanges[$path] = |
49
|
1 |
|
new Change($path, 'Required constraint added to structure', null, $new); |
50
|
1 |
|
break; |
51
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*'): |
52
|
1 |
|
if (!empty($new->required)) { |
53
|
1 |
|
$this->breakingChanges[$path] = |
54
|
1 |
|
new Change($path, 'Required parameter added', null, $new); |
55
|
1 |
|
} elseif (isset($new->in) && $new->in === 'body') { |
56
|
1 |
|
$this->breakingChanges[$path] = new Change($path, 'Body parameter added', null, $new); |
57
|
|
|
} |
58
|
1 |
|
break; |
59
|
|
|
} |
60
|
|
|
} |
61
|
1 |
|
} |
62
|
|
|
|
63
|
1 |
|
private function checkRemovals() |
64
|
|
|
{ |
65
|
1 |
|
foreach ($this->jsonDiff->getRemovedPaths() as $path) { |
66
|
1 |
|
$original = JsonPointer::getByPointer($this->original, $path); |
67
|
|
|
switch (true) { |
68
|
1 |
|
case Path::fitsPattern($path, '#/paths/*'): |
69
|
1 |
|
$this->breakingChanges[$path] = |
70
|
1 |
|
new Change($path, 'Path removed', $original); |
71
|
1 |
|
break; |
72
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*'): |
73
|
1 |
|
$this->breakingChanges[$path] = |
74
|
1 |
|
new Change($path, 'Method removed', $original); |
75
|
1 |
|
break; |
76
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/responses/*'): |
77
|
1 |
|
$this->breakingChanges[$path] = |
78
|
1 |
|
new Change($path, 'Response for http code removed', $original); |
79
|
1 |
|
break; |
80
|
1 |
|
case Path::fitsPattern($path, '#/definitions/*/properties/*'): |
81
|
1 |
|
$this->breakingChanges[$path] = |
82
|
1 |
|
new Change($path, 'Structure property removed', $original); |
83
|
1 |
|
break; |
84
|
|
|
} |
85
|
|
|
} |
86
|
1 |
|
} |
87
|
|
|
|
88
|
1 |
|
private function checkModifications() |
89
|
|
|
{ |
90
|
1 |
|
foreach ($this->jsonDiff->getModifiedPaths() as $path) { |
91
|
1 |
|
$original = JsonPointer::getByPointer($this->original, $path); |
92
|
1 |
|
$new = JsonPointer::getByPointer($this->new, $path); |
93
|
|
|
|
94
|
|
|
switch (true) { |
95
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*/name'): |
96
|
1 |
|
$this->breakingChanges[$path] = |
97
|
1 |
|
new Change($path, 'Missing parameter named ' . $original); |
98
|
1 |
|
break; |
99
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*/required'): |
100
|
1 |
|
if ($new === true) { |
101
|
1 |
|
$this->breakingChanges[$path] = |
102
|
1 |
|
new Change($path, 'Optional parameter became required'); |
103
|
|
|
} |
104
|
1 |
|
break; |
105
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*/in'): |
106
|
1 |
|
$this->breakingChanges[$path] = |
107
|
1 |
|
new Change($path, 'Parameter disposition has changed', $original, $new); |
108
|
1 |
|
break; |
109
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*/type'): |
110
|
1 |
|
$this->breakingChanges[$path] = |
111
|
1 |
|
new Change($path, 'Parameter type has changed', $original, $new); |
112
|
1 |
|
break; |
113
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/parameters/*/schema/...'): |
114
|
1 |
|
$this->breakingChanges[$path] = |
115
|
1 |
|
new Change($path, 'Parameter schema has changed', $original, $new); |
116
|
1 |
|
break; |
117
|
1 |
|
case Path::fitsPattern($path, '#/paths/*/*/responses/*/schema/%24ref'): |
118
|
1 |
|
$this->breakingChanges[$path] = |
119
|
1 |
|
new Change($path, 'Response schema has changed', $original, $new); |
120
|
1 |
|
break; |
121
|
|
|
|
122
|
|
|
|
123
|
|
|
} |
124
|
|
|
} |
125
|
1 |
|
} |
126
|
|
|
|
127
|
|
|
} |