66use cebe \openapi \spec \Operation as OpenAPiOperation ;
77use PhpParser \Builder \Param ;
88use PhpParser \BuilderFactory ;
9+ use PhpParser \Comment \Doc ;
910use PhpParser \Node ;
11+ use PhpParser \Node \Arg ;
1012use PhpParser \Node \Stmt \Class_ ;
1113use Psr \Http \Message \RequestInterface ;
14+ use Psr \Http \Message \ResponseInterface ;
1215use RingCentral \Psr7 \Request ;
16+ use WyriHaximus \Hydrator \Hydrator ;
1317
1418final class Operation
1519{
@@ -21,7 +25,7 @@ final class Operation
2125 * @param OpenAPiOperation $operation
2226 * @return iterable<Node>
2327 */
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
2529 {
2630 $ factory = new BuilderFactory ();
2731 $ stmt = $ factory ->namespace ($ namespace );
@@ -47,10 +51,33 @@ public static function generate(string $path, string $method, string $namespace,
4751 )
4852 )
4953 )
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 ()
5158 );
5259
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+ );
5481 $ requestReplaces = [];
5582 $ query = [];
5683 foreach ($ operation ->parameters as $ parameter ) {
@@ -110,30 +137,133 @@ public static function generate(string $path, string $method, string $namespace,
110137 }
111138 }
112139 $ 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 '
119167 ),
168+ new Node \Name ('validate ' ),
120169 [
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 ' )])),
130172 ]
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
132186 )
133187 )
134188 );
189+
135190 $ 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+ )
137267 );
138268
139269 yield new File ($ namespace . '\\' . $ className , $ stmt ->addStmt ($ class )->getNode ());
0 commit comments