Skip to content

Commit b6a5519

Browse files
author
Kapitanov Andrey
committed
#104744
1 parent 90b8fe5 commit b6a5519

File tree

5 files changed

+143
-7
lines changed

5 files changed

+143
-7
lines changed

src/Generators/PoliciesGenerator.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use cebe\openapi\SpecObjectInterface;
66
use Ensi\LaravelOpenApiServerGenerator\DTO\ParsedRouteHandler;
7+
use Ensi\LaravelOpenApiServerGenerator\Utils\ClassParser;
78
use InvalidArgumentException;
89
use RuntimeException;
910
use stdClass;
@@ -70,6 +71,13 @@ protected function createPoliciesFiles(array $policies, string $template): void
7071
foreach ($policies as ['className' => $className, 'namespace' => $namespace, 'methods' => $methods]) {
7172
$filePath = $this->getNamespacedFilePath($className, $namespace);
7273
if ($this->filesystem->exists($filePath)) {
74+
$class = $this->classParser->parse("$namespace\\$className");
75+
76+
$newPolicies = $this->convertMethodsToString($methods, $class);
77+
if (!empty($newPolicies)) {
78+
$class->addMethods($newPolicies);
79+
}
80+
7381
continue;
7482
}
7583

@@ -104,17 +112,31 @@ private function handlerValidation(ParsedRouteHandler $handler): bool
104112
};
105113
}
106114

107-
private function convertMethodsToString(array $methods): string
115+
private function convertMethodsToString(array $methods, ?ClassParser $class = null): string
108116
{
109117
$methodsStrings = [];
110118

111119
foreach ($methods as $method) {
120+
if ($class?->hasMethod($method)) {
121+
continue;
122+
}
123+
112124
$methodsStrings[] = $this->replacePlaceholders(
113125
$this->templatesManager->getTemplate('PolicyGate.template'),
114126
['{{ method }}' => $method]
115127
);
116128
}
117129

130+
if ($class) {
131+
$existMethods = $class->getMethods();
132+
foreach ($existMethods as $methodName => $method) {
133+
if (!in_array($methodName, $methods) && !$class->isTraitMethod($methodName)) {
134+
$className = $class->getClassName();
135+
console_warning("Warning: метод {$className}::{$methodName} отсутствует в спецификации или не может возвращать 403 ошибку");
136+
}
137+
}
138+
}
139+
118140
return implode("\n\n ", $methodsStrings);
119141
}
120142
}

src/Utils/ClassParser.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class ClassParser
1313
public ReflectionClass $ref;
1414

1515
protected ?Collection $methods = null;
16+
protected ?Collection $traits = null;
1617

1718
public function __construct(
1819
protected Filesystem $filesystem,
@@ -27,6 +28,11 @@ public function parse(string $className): self
2728
return $this;
2829
}
2930

31+
public function getClassName(): string
32+
{
33+
return $this->ref->getName();
34+
}
35+
3036
public function isEmpty(): bool
3137
{
3238
return $this->getMethods()->isEmpty();
@@ -41,6 +47,53 @@ public function getMethods(): Collection
4147
return $this->methods;
4248
}
4349

50+
public function getTraits(): Collection
51+
{
52+
if (!$this->traits) {
53+
$this->traits = collect($this->ref->getTraits())->map(function (ReflectionClass $trait) {
54+
return collect($trait->getMethods())->pluck('name');
55+
});
56+
}
57+
58+
return $this->traits;
59+
}
60+
61+
public function isTraitMethod(string $methodName): bool
62+
{
63+
$traits = $this->getTraits();
64+
65+
return $traits->contains(fn (Collection $methods) => $methods->contains($methodName));
66+
}
67+
68+
public function addMethods(string $methods): void
69+
{
70+
if (empty($methods)) {
71+
return;
72+
}
73+
74+
$lines = [];
75+
$currentLine = 0;
76+
$endLine = $this->getEndLine();
77+
$filePath = $this->getFileName();
78+
79+
foreach ($this->filesystem->lines($filePath) as $line) {
80+
$currentLine++;
81+
if ($currentLine === $endLine) {
82+
$lines[] = "";
83+
$lines[] = " $methods";
84+
$lines[] = "}";
85+
86+
break;
87+
}
88+
89+
$lines[] = $line;
90+
}
91+
92+
$contents = implode(PHP_EOL, $lines);
93+
94+
$this->filesystem->put($filePath, $contents);
95+
}
96+
4497
public function hasMethod(string $methodName): bool
4598
{
4699
return $this->getMethods()->has($methodName);

tests/ControllerParserTest.php renamed to tests/ClassParserTest.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
use Ensi\LaravelOpenApiServerGenerator\Tests\expects\Controllers\LaravelEmptyController;
44
use Ensi\LaravelOpenApiServerGenerator\Tests\expects\Controllers\LaravelExistsController;
5+
use Ensi\LaravelOpenApiServerGenerator\Tests\expects\Policies\LaravelPolicy;
6+
use Ensi\LaravelOpenApiServerGenerator\Tests\expects\Policies\LaravelWithoutTraitPolicy;
57
use Ensi\LaravelOpenApiServerGenerator\Utils\ClassParser;
68
use Illuminate\Filesystem\Filesystem;
79

8-
test('Controller check isEmpty success', function (string $namespace, bool $result) {
10+
test('ClassParser check isEmpty success', function (string $namespace, bool $result) {
911
$filesystem = $this->mock(Filesystem::class);
1012

1113
$parser = new ClassParser($filesystem);
@@ -17,7 +19,7 @@
1719
[LaravelEmptyController::class, true],
1820
]);
1921

20-
test('Controller check getMethods success', function (string $namespace, array $result) {
22+
test('ClassParser check getMethods success', function (string $namespace, array $result) {
2123
$filesystem = $this->mock(Filesystem::class);
2224

2325
$parser = new ClassParser($filesystem);
@@ -31,7 +33,7 @@
3133
[LaravelEmptyController::class, []],
3234
]);
3335

34-
test('Controller check hasMethod success', function (string $namespace, string $method, bool $result) {
36+
test('ClassParser check hasMethod success', function (string $namespace, string $method, bool $result) {
3537
$filesystem = $this->mock(Filesystem::class);
3638

3739
$parser = new ClassParser($filesystem);
@@ -45,7 +47,7 @@
4547
[LaravelEmptyController::class, 'search', false],
4648
]);
4749

48-
test('Controller check getLines success', function (string $namespace, int $start, int $end) {
50+
test('ClassParser check getLines success', function (string $namespace, int $start, int $end) {
4951
$filesystem = $this->mock(Filesystem::class);
5052

5153
$parser = new ClassParser($filesystem);
@@ -58,7 +60,37 @@
5860
[LaravelEmptyController::class, 5, 10],
5961
]);
6062

61-
test('Controller check getFileName success', function (string $namespace) {
63+
test('ClassParser check isTraitMethod success', function (string $namespace, string $method, bool $result) {
64+
$filesystem = $this->mock(Filesystem::class);
65+
66+
$parser = new ClassParser($filesystem);
67+
$parser->parse($namespace);
68+
69+
expect($parser->isTraitMethod($method))->toBe($result);
70+
})->with([
71+
[LaravelPolicy::class, 'allow', true],
72+
[LaravelPolicy::class, 'search', false],
73+
[LaravelPolicy::class, 'get', false],
74+
75+
[LaravelWithoutTraitPolicy::class, 'allow', false],
76+
[LaravelWithoutTraitPolicy::class, 'search', false],
77+
[LaravelWithoutTraitPolicy::class, 'get', false],
78+
]);
79+
80+
test('ClassParser check getClassName success', function (string $namespace) {
81+
$filesystem = $this->mock(Filesystem::class);
82+
83+
$parser = new ClassParser($filesystem);
84+
$parser->parse($namespace);
85+
86+
expect($parser->getClassName())->toBe($namespace);
87+
})->with([
88+
[LaravelPolicy::class],
89+
[LaravelExistsController::class],
90+
[LaravelEmptyController::class],
91+
]);
92+
93+
test('ClassParser check getFileName success', function (string $namespace) {
6294
$filesystem = $this->mock(Filesystem::class);
6395

6496
$parser = new ClassParser($filesystem);
@@ -72,7 +104,7 @@
72104
[LaravelEmptyController::class],
73105
]);
74106

75-
test('Controller check getContentWithAdditionalMethods success', function (
107+
test('ClassParser check getContentWithAdditionalMethods success', function (
76108
string $namespace,
77109
string $expect,
78110
string $additional = "",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Ensi\LaravelOpenApiServerGenerator\Tests\expects\Policies;
4+
5+
use Illuminate\Auth\Access\HandlesAuthorization;
6+
use Illuminate\Auth\Access\Response;
7+
8+
class LaravelPolicy
9+
{
10+
use HandlesAuthorization;
11+
12+
public function search(): Response
13+
{
14+
return Response::allow();
15+
}
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Ensi\LaravelOpenApiServerGenerator\Tests\expects\Policies;
4+
5+
use Illuminate\Auth\Access\Response;
6+
7+
class LaravelWithoutTraitPolicy
8+
{
9+
public function search(): Response
10+
{
11+
return Response::allow();
12+
}
13+
}

0 commit comments

Comments
 (0)