6
6
use cebe \openapi \spec \Operation as OpenAPiOperation ;
7
7
use PhpParser \Builder \Param ;
8
8
use PhpParser \BuilderFactory ;
9
+ use PhpParser \Comment \Doc ;
9
10
use PhpParser \Node ;
11
+ use PhpParser \Node \Arg ;
10
12
use PhpParser \Node \Stmt \Class_ ;
11
13
use Psr \Http \Message \RequestInterface ;
14
+ use Psr \Http \Message \ResponseInterface ;
12
15
use RingCentral \Psr7 \Request ;
16
+ use WyriHaximus \Hydrator \Hydrator ;
13
17
14
18
final class Operation
15
19
{
@@ -21,7 +25,7 @@ final class Operation
21
25
* @param OpenAPiOperation $operation
22
26
* @return iterable<Node>
23
27
*/
24
- public static function generate (string $ path , string $ method , string $ namespace , string $ className , OpenAPiOperation $ operation ): iterable
28
+ public static function generate (string $ path , string $ method , string $ namespace , string $ rootNamespace , string $ className , OpenAPiOperation $ operation, array $ schemaClassNameMap ): iterable
25
29
{
26
30
$ factory = new BuilderFactory ();
27
31
$ stmt = $ factory ->namespace ($ namespace );
@@ -47,10 +51,33 @@ public static function generate(string $path, string $method, string $namespace,
47
51
)
48
52
)
49
53
)
50
- // )->setDocComment('/**' . var_export($operation, true) . '**/');
54
+ )->addStmt (
55
+ $ factory ->property ('requestSchemaValidator ' )->setType ('\League\OpenAPIValidation\Schema\SchemaValidator ' )->makeReadonly ()->makePrivate ()
56
+ )->addStmt (
57
+ $ factory ->property ('responseSchemaValidator ' )->setType ('\League\OpenAPIValidation\Schema\SchemaValidator ' )->makeReadonly ()->makePrivate ()
51
58
);
52
59
53
- $ constructor = $ factory ->method ('__construct ' );
60
+ $ constructor = $ factory ->method ('__construct ' )->makePublic ()->addParam (
61
+ (new Param ('requestSchemaValidator ' ))->setType ('\League\OpenAPIValidation\Schema\SchemaValidator ' )
62
+ )->addStmt (
63
+ new Node \Expr \Assign (
64
+ new Node \Expr \PropertyFetch (
65
+ new Node \Expr \Variable ('this ' ),
66
+ 'requestSchemaValidator '
67
+ ),
68
+ new Node \Expr \Variable ('requestSchemaValidator ' ),
69
+ )
70
+ )->addParam (
71
+ (new Param ('responseSchemaValidator ' ))->setType ('\League\OpenAPIValidation\Schema\SchemaValidator ' )
72
+ )->addStmt (
73
+ new Node \Expr \Assign (
74
+ new Node \Expr \PropertyFetch (
75
+ new Node \Expr \Variable ('this ' ),
76
+ 'responseSchemaValidator '
77
+ ),
78
+ new Node \Expr \Variable ('responseSchemaValidator ' ),
79
+ )
80
+ );
54
81
$ requestReplaces = [];
55
82
$ query = [];
56
83
foreach ($ operation ->parameters as $ parameter ) {
@@ -110,30 +137,133 @@ public static function generate(string $path, string $method, string $namespace,
110
137
}
111
138
}
112
139
$ class ->addStmt ($ constructor );
113
- $ class ->addStmt (
114
- $ factory ->method ('createRequest ' )->setReturnType ('\\' . RequestInterface::class)->addStmt (
115
- new Node \Stmt \Return_ (
116
- new Node \Expr \New_ (
117
- new Node \Name (
118
- '\\' . Request::class
140
+ $ requestParameters = [
141
+ new Node \Arg (new Node \Scalar \String_ ($ method )),
142
+ new Node \Arg (new Node \Expr \FuncCall (
143
+ new Node \Name ('\str_replace ' ),
144
+ [
145
+ new Node \Expr \Array_ (array_map (static fn (string $ key ): Node \Expr \ArrayItem => new Node \Expr \ArrayItem (new Node \Scalar \String_ ($ key )), array_keys ($ requestReplaces ))),
146
+ new Node \Expr \Array_ (array_values ($ requestReplaces )),
147
+ new Node \Scalar \String_ (rtrim ($ path . '? ' . implode ('& ' , $ query ), '? ' )),
148
+ ]
149
+ )),
150
+ ];
151
+
152
+ $ createRequestMethod = $ factory ->method ('createRequest ' )->setReturnType ('\\' . RequestInterface::class)->addParam (
153
+ $ factory ->param ('data ' )->setType ('array ' )->setDefault ([])
154
+ );
155
+
156
+ if ($ operation ->requestBody !== null ) {
157
+ foreach ($ operation ->requestBody ->content as $ requestBodyContentType => $ requestBodyContent ) {
158
+ $ requestParameters [] = new Node \Expr \Array_ ([
159
+ new Node \Expr \ArrayItem (new Node \Scalar \String_ ($ requestBodyContentType ), new Node \Scalar \String_ ('Content-Type ' ))
160
+ ]);
161
+ $ requestParameters [] = new Node \Expr \FuncCall (new Node \Name ('json_encode ' ), [new Arg (new Node \Expr \Variable ('data ' ))]);
162
+ $ createRequestMethod ->addStmt (
163
+ new Node \Stmt \Expression (new Node \Expr \MethodCall (
164
+ new Node \Expr \PropertyFetch (
165
+ new Node \Expr \Variable ('this ' ),
166
+ 'requestSchemaValidator '
119
167
),
168
+ new Node \Name ('validate ' ),
120
169
[
121
- new Node \Arg (new Node \Scalar \String_ ($ method )),
122
- new Node \Arg (new Node \Expr \FuncCall (
123
- new Node \Name ('\str_replace ' ),
124
- [
125
- new Node \Expr \Array_ (array_map (static fn (string $ key ): Node \Expr \ArrayItem => new Node \Expr \ArrayItem (new Node \Scalar \String_ ($ key )), array_keys ($ requestReplaces ))),
126
- new Node \Expr \Array_ (array_values ($ requestReplaces )),
127
- new Node \Scalar \String_ (rtrim ($ path . '? ' . implode ('& ' , $ query ), '? ' )),
128
- ]
129
- )),
170
+ new Node \Arg (new Node \Expr \Variable ('data ' )),
171
+ new Node \Arg (new Node \Expr \StaticCall (new Node \Name ('\cebe\openapi\Reader ' ), new Node \Name ('readFromJson ' ), [new Node \Scalar \String_ (json_encode ($ requestBodyContent ->schema ->getSerializableData ())), new Node \Scalar \String_ ('\cebe\openapi\spec\Schema ' )])),
130
172
]
131
- )
173
+ ))
174
+ );
175
+ break ;
176
+ }
177
+ }
178
+
179
+ $ createRequestMethod ->addStmt (
180
+ new Node \Stmt \Return_ (
181
+ new Node \Expr \New_ (
182
+ new Node \Name (
183
+ '\\' . Request::class
184
+ ),
185
+ $ requestParameters
132
186
)
133
187
)
134
188
);
189
+
135
190
$ class ->addStmt (
136
- $ factory ->method ('validateResponse ' )
191
+ $ createRequestMethod
192
+ );
193
+ $ cases = [];
194
+ $ returnType = [];
195
+ foreach ($ operation ->responses as $ code => $ spec ) {
196
+ $ contentTypeCases = [];
197
+ foreach ($ spec ->content as $ contentType => $ contentTypeSchema ) {
198
+ $ returnType [] = $ object = '\\' . $ rootNamespace . 'Schema \\' . $ schemaClassNameMap [spl_object_hash ($ contentTypeSchema ->schema )];
199
+ $ ctc = new Node \Stmt \Case_ (
200
+ new Node \Scalar \String_ ($ contentType ),
201
+ [
202
+ new Node \Stmt \Expression (new Node \Expr \MethodCall (
203
+ new Node \Expr \PropertyFetch (
204
+ new Node \Expr \Variable ('this ' ),
205
+ 'responseSchemaValidator '
206
+ ),
207
+ new Node \Name ('validate ' ),
208
+ [
209
+ new Node \Arg (new Node \Expr \Variable ('body ' )),
210
+ new Node \Arg (new Node \Expr \StaticCall (new Node \Name ('\cebe\openapi\Reader ' ), new Node \Name ('readFromJson ' ), [new Node \Scalar \String_ (json_encode ($ contentTypeSchema ->schema ->getSerializableData ())), new Node \Scalar \String_ ('\cebe\openapi\spec\Schema ' )])),
211
+ ]
212
+ )),
213
+ new Node \Stmt \Return_ (new Node \Expr \MethodCall (
214
+ new Node \Expr \Variable ('hydrator ' ),
215
+ new Node \Name ('hydrate ' ),
216
+ [
217
+ new Node \Arg (new Node \Scalar \String_ ($ object )),
218
+ new Node \Arg (new Node \Expr \Variable ('body ' )),
219
+ ]
220
+ )),
221
+ // new Node\Stmt\Break_()
222
+ ]
223
+ );
224
+ $ contentTypeCases [] = $ ctc ;
225
+ // $ctc->setDocComment(new Doc('/**' . @var_export($contentTypeSchema->getSerializableData(), true) . '**/'));
226
+ }
227
+ $ case = new Node \Stmt \Case_ (
228
+ new Node \Scalar \LNumber ($ code ),
229
+ [
230
+ new Node \Stmt \Switch_ (
231
+ new Node \Expr \Variable ('contentType ' ),
232
+ $ contentTypeCases
233
+ ),
234
+ new Node \Stmt \Break_ ()
235
+ ]
236
+ );
237
+ $ cases [] = $ case ;
238
+ $ case ->setDocComment (new Doc ('/** ' . $ spec ->description . '**/ ' ));
239
+ // $case->setDocComment(new Doc('/**' . @var_export($spec->getSerializableData(), true) . '**/'));
240
+ }
241
+ $ class ->addStmt (
242
+ $ factory ->method ('createResponse ' )->addParam (
243
+ $ factory ->param ('response ' )->setType ('\\' . ResponseInterface::class)
244
+ )->setReturnType (
245
+ new Node \UnionType (array_map (static fn (string $ object ): Node \Name => new Node \Name ($ object ), array_unique ($ returnType )))
246
+ )->addStmt (
247
+ new Node \Expr \Assign (new Node \Expr \Variable ('contentType ' ), new Node \Expr \MethodCall (new Node \Expr \Variable ('response ' ), 'getHeaderLine ' , [new Arg (new Node \Scalar \String_ ('Content-Type ' ))]))
248
+ )->addStmt (
249
+ new Node \Expr \Assign (new Node \Expr \Variable ('body ' ), new Node \Expr \FuncCall (new Node \Name ('json_decode ' ), [new Node \Expr \MethodCall (new Node \Expr \MethodCall (new Node \Expr \Variable ('response ' ), 'getBody ' ), 'getContents ' ), new Node \Expr \ConstFetch (new Node \Name ('true ' ))]))
250
+ )->addStmt (
251
+ new Node \Expr \Assign (new Node \Expr \Variable ('hydrator ' ), new Node \Expr \New_ (new Node \Name ('\\' . Hydrator::class)))
252
+ )->addStmt (
253
+ new Node \Stmt \Switch_ (
254
+ new Node \Expr \MethodCall (new Node \Expr \Variable ('response ' ), 'getStatusCode ' ),
255
+ $ cases
256
+ )
257
+ )->addStmt (
258
+ new Node \Stmt \Throw_ (
259
+ new Node \Expr \New_ (
260
+ new Node \Name ('\\' . \RuntimeException::class),
261
+ [
262
+ new Arg (new Node \Scalar \String_ ('Unable to find matching reponse code and content type ' ))
263
+ ]
264
+ )
265
+ )
266
+ )
137
267
);
138
268
139
269
yield new File ($ namespace . '\\' . $ className , $ stmt ->addStmt ($ class )->getNode ());
0 commit comments