Skip to content

Commit abac2b2

Browse files
author
Kapitanov Andrey
committed
#104744
1 parent b8c5d29 commit abac2b2

File tree

6 files changed

+124
-9
lines changed

6 files changed

+124
-9
lines changed

src/Commands/GenerateServer.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010

1111
class GenerateServer extends Command
1212
{
13+
public const SUPPORTED_ENTITIES = [
14+
'controllers',
15+
'enums',
16+
'requests',
17+
'routes',
18+
'pest_tests',
19+
'resources',
20+
'policies',
21+
];
22+
1323
/** var @string */
1424
protected $signature = 'openapi:generate-server {--e|entities=}';
1525

@@ -55,7 +65,12 @@ public function handleMapping(string $sourcePath, array $optionsPerEntity)
5565
{
5666
$specObject = $this->parseSpec($sourcePath);
5767

58-
foreach ($this->config['supported_entities'] as $entity => $generatorClass) {
68+
foreach (static::SUPPORTED_ENTITIES as $entity) {
69+
$generatorClass = $this->config['supported_entities'][$entity] ?? null;
70+
if (!isset($generatorClass)) {
71+
continue;
72+
}
73+
5974
if (!$this->shouldEntityBeGenerated($entity)) {
6075
continue;
6176
}

src/Generators/BaseGenerator.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
class BaseGenerator
1616
{
17+
/** @var array Recently created controllers */
18+
public static array $controllers = [];
19+
1720
protected array $options = [];
1821

1922
public function __construct(
@@ -27,6 +30,26 @@ public function __construct(
2730
) {
2831
}
2932

33+
public static function markNewControllerMethod(
34+
string $serversUrl,
35+
string $path,
36+
string $method,
37+
array $responseCodes
38+
): void {
39+
static::$controllers[$serversUrl][$path][$method] = $responseCodes;
40+
}
41+
42+
public static function isExistControllerMethod(
43+
string $serversUrl,
44+
string $path,
45+
string $method,
46+
int $responseCode
47+
): bool {
48+
$codes = static::$controllers[$serversUrl][$path][$method] ?? [];
49+
50+
return !in_array($responseCode, $codes);
51+
}
52+
3053
public function setOptions(array $options): static
3154
{
3255
$this->options = $options;

src/Generators/ControllersGenerator.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ class ControllersGenerator extends BaseGenerator implements GeneratorInterface
1414

1515
private array $methodsWithRequests = ['PATCH', 'POST', 'PUT', 'DELETE'];
1616

17+
private string $serversUrl;
18+
1719
public function generate(SpecObjectInterface $specObject): void
1820
{
21+
$openApiData = $specObject->getSerializableData();
22+
$this->serversUrl = $openApiData?->servers[0]?->url ?? '';
23+
1924
$controllers = $this->extractControllers($specObject);
2025
$this->createControllersFiles($controllers);
2126
}
@@ -26,7 +31,7 @@ private function extractControllers(SpecObjectInterface $specObject): array
2631

2732
$controllers = [];
2833
$paths = $openApiData->paths ?: [];
29-
foreach ($paths as $routes) {
34+
foreach ($paths as $path => $routes) {
3035
foreach ($routes as $method => $route) {
3136
$requestClassName = null;
3237
$methodWithRequest = in_array(strtoupper($method), $this->methodsWithRequests);
@@ -60,10 +65,17 @@ private function extractControllers(SpecObjectInterface $specObject): array
6065
$controllers[$fqcn]['requestsNamespaces'][$namespace] = $namespace;
6166
}
6267

68+
$responses = $route->responses ?? null;
6369
$controllers[$fqcn]['actions'][] = [
6470
'name' => $handler->method ?: '__invoke',
6571
'with_request_namespace' => $methodWithRequest && !empty($route->{'x-lg-skip-request-generation'}),
6672
'parameters' => array_merge($this->extractPathParameters($route), $this->getActionExtraParameters($methodWithRequest, $requestClassName)),
73+
74+
'route' => [
75+
'method' => $method,
76+
'path' => $path,
77+
'responseCodes' => $responses ? array_keys(get_object_vars($responses)) : [],
78+
],
6779
];
6880
}
6981
}
@@ -73,7 +85,7 @@ private function extractControllers(SpecObjectInterface $specObject): array
7385

7486
private function extractPathParameters(stdClass $route): array
7587
{
76-
$oasRoutePath = array_filter($route->parameters ?? [], fn (stdClass $param) => $param?->in === "path");
88+
$oasRoutePath = array_filter($route->parameters ?? [], fn (stdClass $param) => $param?->in === "path");
7789

7890
return array_map(fn (stdClass $param) => [
7991
'name' => $param->name,
@@ -100,7 +112,8 @@ private function createControllersFiles(array $controllers): void
100112
$className = $controller['className'];
101113

102114
$filePath = $this->getNamespacedFilePath($className, $namespace);
103-
if (!$this->filesystem->exists($filePath)) {
115+
$controllerExists = $this->filesystem->exists($filePath);
116+
if (!$controllerExists) {
104117
$this->createEmptyControllerFile($filePath, $controller);
105118
}
106119

@@ -109,6 +122,8 @@ private function createControllersFiles(array $controllers): void
109122
$newMethods = $this->convertMethodsToString($class, $controller['actions'], $controller['requestsNamespaces']);
110123
if (!empty($newMethods)) {
111124
$controller['requestsNamespaces'][static::RESPONSABLE_NAMESPACE] = static::RESPONSABLE_NAMESPACE;
125+
} elseif ($controllerExists) {
126+
continue;
112127
}
113128

114129
$content = $class->getContentWithAdditionalMethods($newMethods, $controller['requestsNamespaces']);
@@ -172,6 +187,13 @@ private function convertMethodsToString(ClassParser $class, array $methods, arra
172187
'{{ params }}' => $this->formatActionParamsAsString($method['parameters']),
173188
]
174189
);
190+
191+
static::markNewControllerMethod(
192+
serversUrl: $this->serversUrl,
193+
path: $method['route']['path'],
194+
method: $method['route']['method'],
195+
responseCodes: $method['route']['responseCodes'],
196+
);
175197
}
176198

177199
$prefix = !empty($methodsStrings) ? static::DELIMITER : '';

src/Generators/PestTestsGenerator.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,27 @@ private function getPhpHttpTestMethodCommon(string $httpMethod): string
3434
return $httpMethod;
3535
}
3636

37-
protected function convertRoutesToTestsString(array $routes, string $serversUrl): string
37+
protected function convertRoutesToTestsString(array $routes, string $serversUrl, bool $onlyNewMethods = false): string
3838
{
39-
$testsFunctions = [
40-
"uses()->group('component');",
41-
];
39+
$testsFunctions = $onlyNewMethods ? [] : ["uses()->group('component');"];
4240

4341
foreach ($routes as $route) {
4442
foreach ($route['responseCodes'] as $responseCode) {
4543
if ($responseCode < 200 || $responseCode >= 500) {
4644
continue;
4745
}
4846

47+
$methodExists = static::isExistControllerMethod(
48+
serversUrl: $serversUrl,
49+
path: $route['path'],
50+
method: $route['method'],
51+
responseCode: $responseCode,
52+
);
53+
54+
if ($onlyNewMethods && ($methodExists || $responseCode >= 300)) {
55+
continue;
56+
}
57+
4958
$url = $serversUrl . $route['path'];
5059
$testName = strtoupper($route['method']) . ' ' . $url. ' ' . $responseCode;
5160
$phpHttpMethod = $this->getPhpHttpTestMethod($route['method'], $route['responseContentType']);

src/Generators/TestsGenerator.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
abstract class TestsGenerator extends BaseGenerator implements GeneratorInterface
1111
{
12-
abstract protected function convertRoutesToTestsString(array $routes, string $serversUrl): string;
12+
abstract protected function convertRoutesToTestsString(array $routes, string $serversUrl, bool $onlyNewMethods = false): string;
1313

1414
abstract protected function convertRoutesToImportsString(array $routes): string;
1515

@@ -95,6 +95,16 @@ protected function createTestsFiles(array $testsData, string $template, $servers
9595
foreach ($testsData as ['className' => $className, 'namespace' => $namespace, 'routes' => $routes]) {
9696
$filePath = $this->getNamespacedFilePath($className, $namespace);
9797
if ($this->filesystem->exists($filePath)) {
98+
$newTests = $this->convertRoutesToTestsString($routes, $serversUrl, true);
99+
if (!empty($newTests)) {
100+
$data = <<<TESTS
101+
{$newTests}
102+
103+
TESTS;
104+
105+
$this->filesystem->append($filePath, $data);
106+
}
107+
98108
continue;
99109
}
100110

tests/GenerateServerTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,39 @@
161161
$routes
162162
);
163163
});
164+
165+
test("Update tests success", function () {
166+
/** @var TestCase $this */
167+
$mapping = Config::get('openapi-server-generator.api_docs_mappings');
168+
$mappingValue = current($mapping);
169+
$mapping = [$this->makeFilePath(__DIR__ . '/resources/index.yaml') => $mappingValue];
170+
Config::set('openapi-server-generator.api_docs_mappings', $mapping);
171+
172+
$existTest = $this->makeFilePath('/app/Http/Tests/ResourcesComponentTest.php');
173+
174+
$filesystem = $this->mock(Filesystem::class);
175+
$filesystem->shouldReceive('exists')->andReturnUsing(function ($path) {
176+
// todo
177+
return false;
178+
});
179+
180+
$filesystem->shouldReceive('get')->withArgs(function ($path) {
181+
return (bool)strstr($path, '.template');
182+
})->andReturnUsing(function ($path) {
183+
return file_get_contents($path);
184+
});
185+
$filesystem->shouldReceive('cleanDirectory', 'ensureDirectoryExists');
186+
$appRoot = realpath($this->makeFilePath(__DIR__ . '/../vendor/orchestra/testbench-core/laravel/'));
187+
$putFiles = [];
188+
$filesystem->shouldReceive('put')->withArgs(function ($path, $content) use (&$putFiles, $appRoot) {
189+
$filePath = $this->makeFilePath(str_replace($appRoot, '', $path));
190+
$putFiles[$filePath] = $filePath;
191+
192+
return true;
193+
});
194+
195+
artisan(GenerateServer::class);
196+
197+
// todo: check exist test
198+
199+
});

0 commit comments

Comments
 (0)