Skip to content

Introduce representation layer for reduced complexity #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/openapi-client-generator.source
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use Symfony\Component\Yaml\Yaml;
*/
exit((function (string $configuration): int {
$yaml = Yaml::parseFile($configuration);
(new Generator($yaml['spec']))->generate($yaml['namespace'] . '\\', $yaml['destination']);
(new Generator($yaml['spec']))->generate($yaml['namespace'] . '\\', dirname($configuration) . DIRECTORY_SEPARATOR . $yaml['destination']);

return 0;
})($configuration));
Expand Down
51 changes: 24 additions & 27 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions src/Files.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace ApiClients\Tools\OpenApiClientGenerator;

use ApiClients\Tools\OpenApiClientGenerator\Generator\Client;
use ApiClients\Tools\OpenApiClientGenerator\Generator\ClientInterface;
use ApiClients\Tools\OpenApiClientGenerator\Generator\Hydrator;
use ApiClients\Tools\OpenApiClientGenerator\Generator\Operation;
use ApiClients\Tools\OpenApiClientGenerator\Generator\Path;
use ApiClients\Tools\OpenApiClientGenerator\Generator\Schema;
use ApiClients\Tools\OpenApiClientGenerator\Generator\WebHook;
use ApiClients\Tools\OpenApiClientGenerator\Generator\WebHooks;
use ApiClients\Tools\OpenApiClientGenerator\Registry\Schema as SchemaRegistry;
use cebe\openapi\Reader;
use cebe\openapi\spec\OpenApi;
use EventSauce\ObjectHydrator\ObjectMapperCodeGenerator;
use Jawira\CaseConverter\Convert;
use PhpParser\Node;
use PhpParser\PrettyPrinter\Standard;

final class Files
{
/**
* @param string $path
* @return iterable<string, string>
*/
public static function listExistingFiles(string $path): iterable
{
if (!file_exists($path)) {
yield from [];
return;
}

foreach (scandir($path) as $node) {
if ($node === '.' || $node === '..') {
continue;
}

if (is_file($path . $node)) {
yield $path . $node => md5_file($path . $node);
}

if (is_dir($path . $node)) {
yield from self::listExistingFiles($path . $node . DIRECTORY_SEPARATOR);
}
}
}
}
42 changes: 42 additions & 0 deletions src/Gatherer/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace ApiClients\Tools\OpenApiClientGenerator\Gatherer;

use ApiClients\Tools\OpenApiClientGenerator\Utils;
use ApiClients\Tools\OpenApiClientGenerator\Registry\Schema as SchemaRegistry;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationRequestBody;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationResponse;
use ApiClients\Tools\OpenApiClientGenerator\Representation\Parameter;
use cebe\openapi\spec\OpenApi;
use cebe\openapi\spec\Operation as openAPIOperation;
use cebe\openapi\spec\PathItem;
use cebe\openapi\spec\Server;
use Jawira\CaseConverter\Convert;
use Psr\Http\Message\ResponseInterface;

final class Client
{
public static function gather(
OpenApi $spec,
\ApiClients\Tools\OpenApiClientGenerator\Representation\Path ...$paths,
): \ApiClients\Tools\OpenApiClientGenerator\Representation\Client {
$baseUrl = null;
foreach ($spec->servers ?? [] as $server) {
if (!($server instanceof Server)) {
continue;
}

if (strlen($server->url) === 0) {
continue;
}

$baseUrl = $server->url;
break;
}

return new \ApiClients\Tools\OpenApiClientGenerator\Representation\Client(
$baseUrl,
$paths,
);
}
}
27 changes: 27 additions & 0 deletions src/Gatherer/Hydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace ApiClients\Tools\OpenApiClientGenerator\Gatherer;

use ApiClients\Tools\OpenApiClientGenerator\Utils;
use ApiClients\Tools\OpenApiClientGenerator\Registry\Schema as SchemaRegistry;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationRequestBody;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationResponse;
use ApiClients\Tools\OpenApiClientGenerator\Representation\Parameter;
use cebe\openapi\spec\Operation as openAPIOperation;
use cebe\openapi\spec\PathItem;
use Jawira\CaseConverter\Convert;
use Psr\Http\Message\ResponseInterface;

final class Hydrator
{
public static function gather(
string $className,
\ApiClients\Tools\OpenApiClientGenerator\Representation\Schema ...$schemaClasses,
): \ApiClients\Tools\OpenApiClientGenerator\Representation\Hydrator {
return new \ApiClients\Tools\OpenApiClientGenerator\Representation\Hydrator(
$className,
str_replace(['\\', '/'], ['/', '🌀'], lcfirst($className)),
$schemaClasses,
);
}
}
102 changes: 102 additions & 0 deletions src/Gatherer/Operation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace ApiClients\Tools\OpenApiClientGenerator\Gatherer;

use ApiClients\Tools\OpenApiClientGenerator\Representation\Hydrator;
use ApiClients\Tools\OpenApiClientGenerator\Utils;
use ApiClients\Tools\OpenApiClientGenerator\Registry\Schema as SchemaRegistry;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationRequestBody;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationResponse;
use ApiClients\Tools\OpenApiClientGenerator\Representation\Parameter;
use cebe\openapi\spec\Operation as openAPIOperation;
use cebe\openapi\spec\PathItem;
use Jawira\CaseConverter\Convert;
use Psr\Http\Message\ResponseInterface;

final class Operation
{
public static function gather(
string $className,
string $method,
string $path,
openAPIOperation $operation,
SchemaRegistry $schemaRegistry,
): \ApiClients\Tools\OpenApiClientGenerator\Representation\Operation {
$returnType = [];
$parameters = [];
$hasPerPageParameter = false;
$hasPageParameter = false;
foreach ($operation->parameters as $parameter) {
if ($parameter->name === 'per_page') {
$hasPerPageParameter = true;
}
if ($parameter->name === 'page') {
$hasPageParameter = true;
}
$parameterType = str_replace([
'integer',
'any',
'boolean',
], [
'int',
'mixed',
'bool',
], implode('|', is_array($parameter->schema->type) ? $parameter->schema->type : [$parameter->schema->type]));

$parameters[] = new Parameter(
$parameter->name,
(string)$parameter->description,
$parameterType,
$parameter->in,
$parameter->schema->default,
);
}

$classNameSanitized = str_replace('/', '\\', Utils::className($className));
$requestBody = [];
if ($operation->requestBody !== null) {
foreach ($operation->requestBody->content as $contentType => $requestBodyDetails) {
$requestBodyClassname = $schemaRegistry->get($requestBodyDetails->schema, $classNameSanitized . '\\Request\\' . Utils::className(str_replace('/', '', $contentType)));
$requestBody[] = new OperationRequestBody(
$contentType,
Schema::gather($requestBodyClassname, $requestBodyDetails->schema, $schemaRegistry),
);
}
}
$response = [];
foreach ($operation->responses as $code => $spec) {
foreach ($spec->content as $contentType => $contentTypeMediaType) {
$responseClassname = $schemaRegistry->get($contentTypeMediaType->schema, 'Operation\\' . $classNameSanitized . '\\Response\\' . Utils::className(str_replace('/', '', $contentType) . '\\H' . $code));
$response[] = new OperationResponse(
$code,
$contentType,
$spec->description,
Schema::gather($responseClassname, $contentTypeMediaType->schema, $schemaRegistry),
);
$returnType[] = $responseClassname;
}
}

if (count($returnType) === 0) {
$returnType[] = '\\' . ResponseInterface::class;
}

return new \ApiClients\Tools\OpenApiClientGenerator\Representation\Operation(
Utils::fixKeyword($className),
$classNameSanitized,
lcfirst(trim(Utils::basename($className),'\\')),
trim(Utils::dirname($className),'\\'),
$operation->operationId,
$method,
$path,
$hasPageParameter === true && $hasPerPageParameter === true, // This is very GitHub specific!!!
array_unique($returnType),
[
...array_filter($parameters, static fn (Parameter $parameter): bool => $parameter->default === null),
...array_filter($parameters, static fn (Parameter $parameter): bool => $parameter->default !== null),
],
$requestBody,
$response,
);
}
}
34 changes: 34 additions & 0 deletions src/Gatherer/OperationHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace ApiClients\Tools\OpenApiClientGenerator\Gatherer;

use ApiClients\Tools\OpenApiClientGenerator\Utils;
use ApiClients\Tools\OpenApiClientGenerator\Registry\Schema as SchemaRegistry;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationRequestBody;
use ApiClients\Tools\OpenApiClientGenerator\Representation\OperationResponse;
use ApiClients\Tools\OpenApiClientGenerator\Representation\Parameter;
use cebe\openapi\spec\Operation as openAPIOperation;
use cebe\openapi\spec\PathItem;
use Jawira\CaseConverter\Convert;
use Psr\Http\Message\ResponseInterface;

final class OperationHydrator
{
public static function gather(
string $className,
\ApiClients\Tools\OpenApiClientGenerator\Representation\Operation ...$operations,
): \ApiClients\Tools\OpenApiClientGenerator\Representation\Hydrator {
$schemaClasses = [];

foreach ($operations as $operation) {
foreach ($operation->response as $response) {
$schemaClasses[] = $response->schema;
}
}

return Hydrator::gather(
'Operation\\' . $className,
...$schemaClasses,
);
}
}
Loading