From eee5ff325b05eb8b1e72afbd1915e1fb3417ea48 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sat, 4 Mar 2023 19:54:43 +0100 Subject: [PATCH] Support passing full URL into call method --- composer.json | 3 +- composer.lock | 24 +-- src/Generator/Client.php | 387 +++++++++++++++++++++++++++++++-------- test.php | 14 ++ 4 files changed, 337 insertions(+), 91 deletions(-) create mode 100644 test.php diff --git a/composer.json b/composer.json index 35c0e72..4d17e67 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ }, "autoload": { "psr-4": { - "ApiClients\\Tools\\OpenApiClientGenerator\\": "src/" + "ApiClients\\Tools\\OpenApiClientGenerator\\": "src/", + "ApiClients\\Client\\Github\\": "generated/" }, "files": [ "external_files/cebe/JsonReference.php", diff --git a/composer.lock b/composer.lock index 5a9ac55..17ea0ce 100644 --- a/composer.lock +++ b/composer.lock @@ -2045,16 +2045,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.0", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", "shasum": "" }, "require": { @@ -2092,7 +2092,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" }, "funding": [ { @@ -2108,7 +2108,7 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2023-03-01T10:25:55+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2277,16 +2277,16 @@ }, { "name": "symfony/yaml", - "version": "v5.4.19", + "version": "v5.4.21", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5" + "reference": "3713e20d93e46e681e51605d213027e48dab3469" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/71c05db20cb9b54d381a28255f17580e2b7e36a5", - "reference": "71c05db20cb9b54d381a28255f17580e2b7e36a5", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3713e20d93e46e681e51605d213027e48dab3469", + "reference": "3713e20d93e46e681e51605d213027e48dab3469", "shasum": "" }, "require": { @@ -2332,7 +2332,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.19" + "source": "https://github.com/symfony/yaml/tree/v5.4.21" }, "funding": [ { @@ -2348,7 +2348,7 @@ "type": "tidelift" } ], - "time": "2023-01-10T18:51:14+00:00" + "time": "2023-02-21T19:46:44+00:00" }, { "name": "twig/twig", diff --git a/src/Generator/Client.php b/src/Generator/Client.php index 759b191..18c8c8c 100644 --- a/src/Generator/Client.php +++ b/src/Generator/Client.php @@ -2,6 +2,7 @@ namespace ApiClients\Tools\OpenApiClientGenerator\Generator; +use ApiClients\Client\Github\Schema\WebhookLabelEdited\Changes\Name; use ApiClients\Contracts\HTTP\Headers\AuthenticationInterface; use ApiClients\Contracts\OpenAPI\WebHooksInterface; use ApiClients\Tools\OpenApiClientGenerator\File; @@ -27,6 +28,7 @@ use RingCentral\Psr7\Request; use Rx\Observable; use Twig\Node\Expression\Binary\AndBinary; +use Twig\Node\Expression\Binary\OrBinary; final class Client { @@ -209,18 +211,93 @@ public static function generate(string $namespace, \ApiClients\Tools\OpenApiClie } else { $operationPath = explode('/', $operation->path); } + $operationPathCount = count($operationPath); if (!array_key_exists($operation->method, $sortedOperations)) { - $sortedOperations[$operation->method] = [ + $sortedOperations[$operation->method] = []; + } + if (!array_key_exists($operationPathCount, $sortedOperations[$operation->method])) { + $sortedOperations[$operation->method][$operationPathCount] = [ 'operations' => [], 'paths' => [], ]; } - $sortedOperations[$operation->method] = self::traverseOperationPaths($sortedOperations[$operation->method], $operationPath, $operation, $path); + $sortedOperations[$operation->method][$operationPathCount] = self::traverseOperationPaths($sortedOperations[$operation->method][$operationPathCount], $operationPath, $operation, $path); } } + +// new Node\Stmt\Switch_( +// new Node\Expr\Variable('method'), +// iterator_to_array((function (array $sortedOperations) use ($factory): iterable { +// foreach ($sortedOperations as $method => $operation) { +// yield new Node\Stmt\Case_( +// new Node\Scalar\String_($method), +// [ +// ...self::traverseOperations($operation['operations'], $operation['paths'], 0), +// new Node\Stmt\Break_(), +// ], +// ); +// } +// })($sortedOperations)) +// ) + + $operationsIfs = []; + foreach ($sortedOperations as $method => $ops) { + $opsTmts = []; + foreach ($ops as $chunkCount => $moar) { + $opsTmts[] = [ + new Node\Expr\BinaryOp\Identical( + new Node\Expr\Variable('pathChunksCount'), + new Node\Scalar\LNumber($chunkCount), + ), + self::traverseOperations($moar['operations'], $moar['paths'], 0), + ]; + } + $operationsIfs[] = [ + new Node\Expr\BinaryOp\Identical( + new Node\Expr\Variable('method'), + new Node\Scalar\String_($method), + ), + (static function (array $opsTmts): array { + $first = array_shift($opsTmts); + $elseIfs = []; + + foreach ($opsTmts as $opsTmt) { + $elseIfs[] = new Node\Stmt\ElseIf_(...$opsTmt); + } + + return [ + new Node\Stmt\If_( + $first[0], + [ + 'stmts' => $first[1], + 'elseifs' => $elseIfs, + ], + ) + ]; + })($opsTmts), + ]; + } + + $firstOperationsIfs = array_shift($operationsIfs); + $operationsIf = new Node\Stmt\If_( + $firstOperationsIfs[0], + [ + 'stmts' => $firstOperationsIfs[1], + 'elseifs' => (static function (array $operationsIfs): array { + $elseIfs = []; + + foreach ($operationsIfs as $operationsIf) { + $elseIfs[] = new Node\Stmt\ElseIf_(...$operationsIf); + } + + return $elseIfs; + })($operationsIfs), + ], + ); + $class->addStmt( $factory->method('callAsync')->makePublic()->setDocComment( new Doc(implode(PHP_EOL, [ @@ -244,6 +321,90 @@ public static function generate(string $namespace, \ApiClients\Tools\OpenApiClie ' */', ])) )->addParam((new Param('call'))->setType('string'))->addParam((new Param('params'))->setType('array')->setDefault([]))->addStmt( + new Node\Expr\Assign( + new Node\Expr\Variable('resolvedUrlPassed'), + new Node\Expr\ConstFetch( + new Node\Name('false'), + ) + ) + )->addStmt( + new Node\Stmt\If_( + new Node\Expr\BinaryOp\Identical( + new Node\Expr\FuncCall( + new Node\Name('strpos'), + [ + new Arg( + new Node\Expr\Variable('call'), + ), + new Arg( + new Node\Scalar\String_($client->baseUrl), + ), + ], + ), + new Node\Scalar\LNumber(0), + ), + [ + 'stmts' => [ + new Node\Stmt\Expression( + new Node\Expr\Assign( + new Node\Expr\Variable('call'), + new Node\Expr\FuncCall( + new Node\Name('substr'), + [ + new Arg( + new Node\Expr\Variable('call'), + ), + new Arg( + new Node\Scalar\LNumber(strlen($client->baseUrl)), + ), + ], + ), + ), + ), + ], + ], + ) + )->addStmt( + new Node\Stmt\If_( + new Node\Expr\BinaryOp\Identical( + new Node\Expr\FuncCall( + new Node\Name('strpos'), + [ + new Arg( + new Node\Expr\Variable('call'), + ), + new Arg( + new Node\Scalar\String_(' '), + ), + ], + ), + new Node\Expr\ConstFetch( + new Node\Name('false'), + ), + ), + [ + 'stmts' => [ + new Node\Stmt\Expression( + new Node\Expr\Assign( + new Node\Expr\Variable('call'), + new Node\Expr\BinaryOp\Concat( + new Node\Scalar\String_('GET '), + new Node\Expr\Variable('call'), + ), + ), + ), + new Node\Stmt\Expression( + new Node\Expr\Assign( + new Node\Expr\Variable('resolvedUrlPassed'), + new Node\Expr\ConstFetch( + new Node\Name('true'), + ) + ), + ), + ], + ], + ) + )->addStmt( new Node\Expr\Assign( new Node\Expr\Array_([ new Node\Expr\ArrayItem( @@ -282,20 +443,19 @@ public static function generate(string $namespace, \ApiClients\Tools\OpenApiClie ], ) ) - )->addStmt(new Node\Stmt\Switch_( - new Node\Expr\Variable('method'), - iterator_to_array((function (array $sortedOperations) use ($factory): iterable { - foreach ($sortedOperations as $method => $operation) { - yield new Node\Stmt\Case_( - new Node\Scalar\String_($method), - [ - ...self::traverseOperations($operation['operations'], $operation['paths'], 0), - new Node\Stmt\Break_(), - ], - ); - } - })($sortedOperations)) - ))->addStmt( + )->addStmt( + new Node\Expr\Assign( + new Node\Expr\Variable('pathChunksCount'), + new Node\Expr\FuncCall( + new Node\Name('count'), + [ + new Arg( + new Node\Expr\Variable('pathChunks'), + ), + ], + ) + ) + )->addStmt($operationsIf)->addStmt( new Node\Stmt\Throw_( new Node\Expr\New_( new Node\Name('\InvalidArgumentException') @@ -342,48 +502,81 @@ private static function traverseOperationPaths(array $operations, array &$operat private static function traverseOperations(array $operations, array $paths, int $level): array { + $nonArgumentPathChunks = []; + foreach ($paths as $pathChunk => $_) { + if (strpos($pathChunk, '{') === 0) { + continue; + } + + $nonArgumentPathChunks[] = new Node\Expr\ArrayItem(new Node\Scalar\String_($pathChunk)); + } + $ifs = []; foreach ($operations as $operation) { $ifs[] = [ new Node\Expr\BinaryOp\Equal( - new Node\Scalar\String_($operation['operation']->method . ' ' . $operation['operation']->path), new Node\Expr\Variable('call'), + new Node\Scalar\String_($operation['operation']->method . ' ' . $operation['operation']->path), ), - [ - 'stmts' => static::callOperation(...$operation), - ] + static::callOperation(...$operation), ]; } foreach ($paths as $pathChunk => $path) { + $baseCondition = new Node\Expr\BinaryOp\Equal( + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('pathChunks'), + new Node\Scalar\LNumber($level), + ), + new Node\Scalar\String_($pathChunk), + ); $ifs[] = [ - new Node\Expr\BinaryOp\BooleanAnd( - new Node\Expr\BinaryOp\Equal( - new Node\Expr\FuncCall( - new Node\Name('array_key_exists'), - [ - new Arg( - new Node\Scalar\LNumber($level), - ), - new Arg( - new Node\Expr\Variable('pathChunks'), + (!(strpos($pathChunk, '{') === 0)) ? $baseCondition : new Node\Expr\BinaryOp\BooleanOr( + new Node\Expr\BooleanNot( + new Node\Expr\BooleanNot( + new Node\Expr\BinaryOp\BooleanAnd( + new Node\Expr\BinaryOp\Equal( + new Node\Expr\Variable('resolvedUrlPassed'), + new Node\Expr\ConstFetch( + new Node\Name('false') + ), ), - ], - ), - new Node\Expr\ConstFetch( - new Node\Name('true'), + $baseCondition, + ), ), ), - new Node\Expr\BinaryOp\Equal( - new Node\Scalar\String_($pathChunk), - new Node\Expr\ArrayDimFetch( - new Node\Expr\Variable('pathChunks'), - new Node\Scalar\LNumber($level), + new Node\Expr\BooleanNot( + new Node\Expr\BooleanNot( + new Node\Expr\BinaryOp\BooleanAnd( + new Node\Expr\BinaryOp\Equal( + new Node\Expr\Variable('resolvedUrlPassed'), + new Node\Expr\ConstFetch( + new Node\Name('true') + ), + ), + new Node\Expr\BinaryOp\NotIdentical( + new Node\Expr\FuncCall( + new Node\Name('in_array'), + [ + new Arg( + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('pathChunks'), + new Node\Scalar\LNumber($level), + ), + ), + new Arg( + new Node\Expr\Array_($nonArgumentPathChunks), + ), + ], + ), + new Node\Expr\ConstFetch( + new Node\Name('true'), + ), + ) + ), ), ), ), - [ - 'stmts' => self::traverseOperations($path['operations'], $path['paths'], $level + 1), - ], + self::traverseOperations($path['operations'], $path['paths'], $level + 1), ]; } @@ -394,13 +587,13 @@ private static function traverseOperations(array $operations, array $paths, int $elfseIfs = []; $baseIf = array_shift($ifs); foreach ($ifs as $if) { - $elfseIfs[] = new Node\Stmt\ElseIf_($if[0], $if[1]['stmts']); + $elfseIfs[] = new Node\Stmt\ElseIf_($if[0], $if[1]); } return [new Node\Stmt\If_( $baseIf[0], [ - 'stmts' => $baseIf[1]['stmts'], + 'stmts' => $baseIf[1], 'elseifs' => $elfseIfs, ], )]; @@ -414,46 +607,84 @@ private static function callOperation(\ApiClients\Tools\OpenApiClientGenerator\R new Node\Expr\Variable('requestBodyData'), new Node\Expr\Array_(), )), - new Node\Stmt\Foreach_(new Node\Expr\FuncCall( - new Node\Name('\array_keys'), + new Node\Stmt\If_( + new Node\Expr\BinaryOp\Identical( + new Node\Expr\Variable('resolvedUrlPassed'), + new Node\Expr\ConstFetch( + new Node\Name('false'), + ), + ), [ - new Arg(new Node\Expr\Variable(new Node\Name('params'))), - ], - ), new Node\Expr\Variable(new Node\Name('param')), [ - 'stmts' => [ - new Node\Stmt\If_( - new Node\Expr\BinaryOp\NotEqual( - new Node\Expr\FuncCall( - new Node\Name('\in_array'), - [ - new Arg(new Node\Expr\Variable(new Node\Name('param'))), - new Arg(new Node\Expr\Array_( - iterator_to_array((function (array $params): iterable { - foreach ($params as $param) { - yield new Node\Expr\ArrayItem(new Node\Scalar\String_($param->name)); - } - })($operation->parameters)), - )), - ], - ), - new Node\Expr\ConstFetch(new Node\Name('false')) - ), - [ + 'stmts' => [ + new Node\Stmt\Foreach_(new Node\Expr\FuncCall( + new Node\Name('\array_keys'), + [ + new Arg(new Node\Expr\Variable(new Node\Name('params'))), + ], + ), new Node\Expr\Variable(new Node\Name('param')), [ 'stmts' => [ - new Node\Stmt\Expression( - new Node\Expr\FuncCall( - new Node\Name('\array_push'), - [ - new Arg(new Node\Expr\Variable(new Node\Name('requestBodyData'))), - new Arg(new Node\Expr\Variable(new Node\Name('param'))), - ], + new Node\Stmt\If_( + new Node\Expr\BinaryOp\NotEqual( + new Node\Expr\FuncCall( + new Node\Name('\in_array'), + [ + new Arg(new Node\Expr\Variable(new Node\Name('param'))), + new Arg(new Node\Expr\Array_( + iterator_to_array((function (array $params): iterable { + foreach ($params as $param) { + yield new Node\Expr\ArrayItem(new Node\Scalar\String_($param->name)); + } + })($operation->parameters)), + )), + ], + ), + new Node\Expr\ConstFetch(new Node\Name('false')) ), + [ + 'stmts' => [ + new Node\Stmt\Expression( + new Node\Expr\Assign( + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('requestBodyData'), + new Node\Expr\Variable('param'), + ), + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('params'), + new Node\Expr\Variable('param'), + ), + ), + ), + ], + ] ), ], - ] - ), + ]), + ], + 'else' => new Node\Stmt\Else_((static function (string $path): array { + $pathChunks = explode('/', $path); + $tmts = []; + + foreach ($pathChunks as $index => $pathChunk) { + if (strpos($pathChunk, '{') === 0) { + $tmts[] = new Node\Stmt\Expression( + new Node\Expr\Assign( + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('requestBodyData'), + new Node\Scalar\String_(substr($pathChunk, 1, -1)), + ), + new Node\Expr\ArrayDimFetch( + new Node\Expr\Variable('pathChunks'), + new Node\Scalar\LNumber($index), + ), + ) + ); + } + } + + return $tmts; + })($operation->path)), ], - ]), + ), ...(implode('|', $operation->returnType) === ('\\' . ResponseInterface::class) ? [] : [new Node\Stmt\If_( new Node\Expr\BinaryOp\Equal( new Node\Expr\FuncCall( diff --git a/test.php b/test.php new file mode 100644 index 0000000..15e5744 --- /dev/null +++ b/test.php @@ -0,0 +1,14 @@ +call('https://api.github.com/repos/octocat/Hello-World/pulls/1347');