Skip to content

Commit 3090339

Browse files
committed
#93471
1 parent 35038bd commit 3090339

File tree

9 files changed

+180
-93
lines changed

9 files changed

+180
-93
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,13 @@ Generates Resource file for `x-lg-handler`
107107
Resource properties are generated relative to field in response, which can be set in the config
108108
```php
109109
'resources' => [
110-
'response_key' => ['data']
110+
'response_key' => 'data'
111111
],
112112
```
113-
You can also specify `response_key` for endpoint using `x-lg-resource-response-key: data`
113+
You can also specify `response_key` for resource: add `x-lg-resource-response-key: data` in object.
114114
When specifying `response_key`, you can use the "dot" syntax to specify nesting, for example `data.field`
115-
You can exclude resource generation using `x-lg-skip-resource-generation: true`.
115+
You can exclude resource generation using `x-lg-skip-resource-generation: true` in route.
116+
You can rename resource Class using `x-lg-resource-class-name: FooResource` in object.
116117
If a resource file already exists it is NOT overriden.
117118
Resource file contains a set of fields according to the specification.
118119
You also need to specify mixin DocBlock to autocomplete resource.

src/Generators/BaseGenerator.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ protected function getReplacedClassName(?string $baseClassName, string $replaceF
7070
return null;
7171
}
7272

73+
protected function replace(string $base, string $from, string $to): ?string
74+
{
75+
if (!str_contains($base, $from)) {
76+
return null;
77+
}
78+
79+
return str_replace($from, $to, $base);
80+
}
81+
7382
protected function getNamespacedFilePath(string $fileName, ?string $namespace): string
7483
{
7584
$toDir = $this->psr4PathConverter->namespaceToPath($namespace);
@@ -96,13 +105,4 @@ private function formattedGlobalParams(): array
96105

97106
return $params;
98107
}
99-
100-
protected function replace(string $base, string $from, string $to): ?string
101-
{
102-
if (!str_contains($base, $from)) {
103-
return null;
104-
}
105-
106-
return str_replace($from, $to, $base);
107-
}
108108
}

src/Generators/ResourcesGenerator.php

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,21 @@ protected function extractResources(SpecObjectInterface $specObject): array
3333
continue;
3434
}
3535

36+
$response = $route->responses->{201} ?? $route->responses->{200} ?? null;
37+
if (!$response) {
38+
continue;
39+
}
40+
41+
$responseSchema = $response->content?->{'application/json'}?->schema ?? null;
42+
if (!$responseSchema) {
43+
continue;
44+
}
45+
3646
$handler = $this->routeHandlerParser->parse($route->{'x-lg-handler'});
3747

3848
try {
3949
$namespace = $this->getReplacedNamespace($handler->namespace, $replaceFrom, $replaceTo);
40-
$className = $route->{'x-lg-resource-class-name'} ?? $this->getReplacedClassName($handler->class, $replaceFrom, $replaceTo);
50+
$className = $responseSchema->{'x-lg-resource-class-name'} ?? $this->getReplacedClassName($handler->class, $replaceFrom, $replaceTo);
4151
} catch (RuntimeException) {
4252
continue;
4353
}
@@ -46,31 +56,35 @@ protected function extractResources(SpecObjectInterface $specObject): array
4656
continue;
4757
}
4858

49-
$response = $route->responses->{201} ?? $route->responses->{200} ?? null;
50-
if (!$response) {
51-
continue;
52-
}
53-
54-
$responseData = $response->content?->{'application/json'}?->schema?->properties ?? null;
55-
56-
$responseKey = $route->{'x-lg-resource-response-key'} ?? null;
57-
if (!$responseKey) {
58-
$responseKey = $this->options['response_key'] ?? '';
59-
}
60-
61-
$responseKeyParts = explode('.', $responseKey);
62-
foreach ($responseKeyParts as $responseKeyPart) {
63-
$responseData = $responseData?->$responseKeyPart ?? null;
59+
$responseData = $responseSchema;
60+
61+
$responseKey = $responseSchema->{'x-lg-resource-response-key'} ??
62+
$this->options['resources']['response_key'] ??
63+
null;
64+
if ($responseKey) {
65+
$responseKeyParts = explode('.', $responseKey);
66+
foreach ($responseKeyParts as $responseKeyPart) {
67+
$flag = false;
68+
do_with_all_of($responseData, function (stdClass $p) use (&$responseData, &$flag, $responseKeyPart) {
69+
if (std_object_has($p, 'properties')) {
70+
if (std_object_has($p->properties, $responseKeyPart)) {
71+
$responseData = $p->properties->$responseKeyPart;
72+
$flag = true;
73+
}
74+
}
75+
});
76+
if (!$flag) {
77+
$responseData = null;
78+
79+
break;
80+
}
81+
}
6482
}
6583

6684
if (!$responseData) {
6785
continue;
6886
}
6987

70-
if (isset($responseData->type) && $responseData->type == 'array') {
71-
$responseData = $responseData->items;
72-
}
73-
7488
$properties = $this->convertToString($this->getProperties($responseData));
7589

7690
if (empty($properties)) {
@@ -103,27 +117,21 @@ protected function createResourcesFiles(array $resources, string $template): voi
103117
}
104118
}
105119

106-
private function getProperties(stdClass $responseData): array
120+
private function getProperties(stdClass $object): array
107121
{
108-
if (isset($responseData->allOf)) {
109-
$properties = [];
122+
$properties = [];
110123

111-
/** @var stdClass $partResponseData */
112-
foreach ($responseData->allOf as $partResponseData) {
113-
if (!isset($partResponseData->properties)) {
114-
continue;
115-
}
116-
$properties = array_merge($properties, $this->getProperties($partResponseData));
124+
do_with_all_of($object, function (stdClass $p) use (&$properties) {
125+
if (std_object_has($p, 'properties')) {
126+
$properties = array_merge($properties, array_keys(get_object_vars($p->properties)));
117127
}
118128

119-
return $properties;
120-
}
121-
122-
if (!isset($responseData->properties)) {
123-
return [];
124-
}
129+
if (std_object_has($p, 'items')) {
130+
$properties = array_merge($properties, $this->getProperties($p->items));
131+
}
132+
});
125133

126-
return array_keys(get_object_vars($responseData->properties));
134+
return $properties;
127135
}
128136

129137
private function convertToString(array $properties): string

src/helpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function do_with_all_of(stdClass $object, callable $fn): void
1313
$fn($object);
1414
if (std_object_has($object, 'allOf')) {
1515
foreach ($object->allOf as $allOfItem) {
16-
$fn($allOfItem);
16+
do_with_all_of($allOfItem, $fn);
1717
}
1818
}
1919
}

tests/GenerateServerTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
$this->makeFilePath('/app/Http/Requests/TestFullGenerateRequest.php'),
4242
$this->makeFilePath('/app/Http/Tests/ResourcesComponentTest.php'),
4343
$this->makeFilePath('/app/Http/Requests/TestFooRenameRequest.php'),
44+
4445
$this->makeFilePath('/app/Http/Requests/LaravelValidationsApplicationJsonRequest.php'),
4546
$this->makeFilePath('/app/Http/Requests/LaravelValidationsMultipartFormDataRequest.php'),
4647
$this->makeFilePath('/app/Http/Requests/LaravelValidationsNonAvailableContentTypeRequest.php'),
47-
$this->makeFilePath('/app/Http/Resources/ResourcesResource.php'),
4848

4949
$this->makeFilePath('/app/Http/Controllers/WithoutResponsesController.php'),
5050

@@ -54,6 +54,10 @@
5454

5555
$this->makeFilePath('/app/Http/ApiV1/OpenApiGenerated/Enums/TestIntegerEnum.php'),
5656
$this->makeFilePath('/app/Http/ApiV1/OpenApiGenerated/Enums/TestStringEnum.php'),
57+
58+
$this->makeFilePath('/app/Http/Resources/ResourcesResource.php'),
59+
$this->makeFilePath('/app/Http/Resources/ResourcesDataDataResource.php'),
60+
$this->makeFilePath('/app/Http/Resources/ResourceRootResource.php'),
5761
], $putFiles);
5862
});
5963

tests/ResourceGenerationTest.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,26 @@
2222
return file_get_contents($path);
2323
});
2424
$filesystem->shouldReceive('cleanDirectory', 'ensureDirectoryExists');
25-
$resource = null;
26-
$filesystem->shouldReceive('put')->withArgs(function ($path, $content) use (&$resource) {
27-
if (str_contains($path, 'ResourcesResource.php')) {
28-
$resource = $content;
25+
$resources = [];
26+
$filesystem->shouldReceive('put')->withArgs(function ($path, $content) use (&$resources) {
27+
if (str_contains($path, 'Resource.php')) {
28+
$resources[pathinfo($path, PATHINFO_BASENAME)] = $content;
2929
}
3030

3131
return true;
3232
});
33-
$propertiesInResource = ['foo', 'bar'];
33+
3434

3535
artisan(GenerateServer::class);
3636

3737
// С помощью регулярки достаем все выражения в кавычках
38-
preg_match_all('~[\'](.*)[\']~', $resource, $matches);
38+
foreach ($resources as $key => $content) {
39+
$matches = [];
40+
preg_match_all('~[\'](.*)[\']~', $content, $matches);
41+
$resources[$key] = $matches[1];
42+
}
3943

40-
assertEqualsCanonicalizing($propertiesInResource, $matches[1]);
44+
assertEqualsCanonicalizing(['foo', 'bar'], $resources['ResourcesResource.php']);
45+
assertEqualsCanonicalizing(['foo', 'bar'], $resources['ResourcesDataDataResource.php']);
46+
assertEqualsCanonicalizing(['data'], $resources['ResourceRootResource.php']);
4147
});

tests/resources/index.yaml

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ paths:
1919
content:
2020
application/json:
2121
schema:
22-
$ref: 'schemas/resources.yaml#/ResourceForTestResourceGenerationResponse'
22+
$ref: './schemas/test_resource_generation.yaml#/ResourceForTestResourceGenerationResponse'
2323
"500":
2424
$ref: '#/components/responses/ServerError'
2525
/resources:test-empty-rename-request:
@@ -29,15 +29,23 @@ paths:
2929
x-lg-request-class-name: ''
3030
responses:
3131
"200":
32-
description: Успешный ответ
32+
description: Успешный ответ c контекстом
33+
content:
34+
application/json:
35+
schema:
36+
$ref: './schemas/test_resource_generation.yaml#/ResourceDataDataResponse'
3337
/resources:test-rename-request:
3438
post:
3539
operationId: testRenameRequest
3640
x-lg-handler: '\App\Http\Controllers\ResourcesController@testRenameRequest'
3741
x-lg-request-class-name: 'TestFooRenameRequest'
3842
responses:
3943
"200":
40-
description: Успешный ответ
44+
description: Успешный ответ c контекстом
45+
content:
46+
application/json:
47+
schema:
48+
$ref: './schemas/test_resource_generation.yaml#/ResourceRootResponse'
4149
/resources:test-without-handler:
4250
post:
4351
operationId: testWithoutHandler
@@ -48,6 +56,7 @@ paths:
4856
post:
4957
operationId: testWithSkip
5058
x-lg-handler: '\App\Http\Controllers\SkipController@testWithSkip'
59+
x-lg-skip-resource-generation: true
5160
x-lg-skip-controller-generation: true
5261
x-lg-skip-request-generation: true
5362
x-lg-skip-tests-generation: true
@@ -82,7 +91,7 @@ paths:
8291
content:
8392
application/json:
8493
schema:
85-
$ref: './schemas/resources.yaml#/ResourceForTestValidationRules'
94+
$ref: './schemas/test_generation_request_validation.yaml#/ResourceForTestValidationRules'
8695
responses:
8796
"200":
8897
description: Успешный ответ
@@ -113,6 +122,30 @@ paths:
113122
responses:
114123
"200":
115124
description: Успешный ответ
125+
/resources:test-generate-resource-bad-response-key:
126+
post:
127+
operationId: generateResourceBadResponseKey
128+
x-lg-handler: '\App\Http\Controllers\ResourcesController@testGenerateResourceBadResponseKey'
129+
x-lg-skip-request-generation: true
130+
responses:
131+
"200":
132+
description: Успешный ответ c контекстом
133+
content:
134+
application/json:
135+
schema:
136+
$ref: './schemas/test_resource_generation.yaml#/GenerateResourceBadResponseKeyResponse'
137+
/resources:test-generate-without-properties:
138+
post:
139+
operationId: generateResourceWithoutProperties
140+
x-lg-handler: '\App\Http\Controllers\ResourcesController@testGenerateResourceWithoutProperties'
141+
x-lg-skip-request-generation: true
142+
responses:
143+
"200":
144+
description: Успешный ответ c контекстом
145+
content:
146+
application/json:
147+
schema:
148+
$ref: './schemas/test_resource_generation.yaml#/GenerateResourceWithoutPropertiesResponse'
116149

117150
components:
118151
responses:

tests/resources/schemas/resources.yaml renamed to tests/resources/schemas/test_generation_request_validation.yaml

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -211,36 +211,7 @@ ResourceRequiredForTestValidationRules:
211211

212212
#Requests
213213
ResourceForTestValidationRules:
214-
allOf:
215-
- $ref: '#/ResourceReadonlyForTestValidationRules'
216-
- $ref: '#/ResourceFillableForTestValidationRules'
217-
- $ref: '#/ResourceRequiredForTestValidationRules'
218-
ResourceForTestResourceGeneration:
219214
allOf:
220-
- $ref: '#/ResourceReadOnlyProperties'
221-
- $ref: '#/ResourceFillableProperties'
222-
- $ref: '#/ResourceRequired'
223-
224-
ResourceReadOnlyProperties:
225-
type: object
226-
properties:
227-
foo:
228-
type: string
229-
230-
ResourceFillableProperties:
231-
type: object
232-
properties:
233-
bar:
234-
type: string
235-
236-
ResourceRequired:
237-
type: object
238-
required:
239-
- foo
240-
241-
ResourceForTestResourceGenerationResponse:
242-
type: object
243-
properties:
244-
data:
245-
$ref: '#/ResourceForTestResourceGeneration'
246-
215+
- $ref: '#/ResourceReadonlyForTestValidationRules'
216+
- $ref: '#/ResourceFillableForTestValidationRules'
217+
- $ref: '#/ResourceRequiredForTestValidationRules'

0 commit comments

Comments
 (0)