14
14
namespace ApiPlatform \Core \JsonSchema ;
15
15
16
16
use ApiPlatform \Core \Api \OperationType ;
17
+ use ApiPlatform \Core \Api \ResourceClassResolverInterface ;
17
18
use ApiPlatform \Core \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
18
19
use ApiPlatform \Core \Metadata \Property \Factory \PropertyNameCollectionFactoryInterface ;
19
20
use ApiPlatform \Core \Metadata \Property \PropertyMetadata ;
20
21
use ApiPlatform \Core \Metadata \Resource \Factory \ResourceMetadataFactoryInterface ;
21
22
use ApiPlatform \Core \Metadata \Resource \ResourceMetadata ;
22
23
use ApiPlatform \Core \Swagger \Serializer \DocumentationNormalizer ;
24
+ use ApiPlatform \Core \Util \ResourceClassInfoTrait ;
23
25
use Symfony \Component \PropertyInfo \Type ;
24
26
use Symfony \Component \Serializer \NameConverter \NameConverterInterface ;
25
27
use Symfony \Component \Serializer \Normalizer \AbstractNormalizer ;
33
35
*/
34
36
final class SchemaFactory implements SchemaFactoryInterface
35
37
{
36
- private $ resourceMetadataFactory ;
38
+ use ResourceClassInfoTrait;
39
+
40
+ private $ typeFactory ;
37
41
private $ propertyNameCollectionFactory ;
38
42
private $ propertyMetadataFactory ;
39
- private $ typeFactory ;
40
43
private $ nameConverter ;
41
44
private $ distinctFormats = [];
42
45
43
- public function __construct (TypeFactoryInterface $ typeFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , NameConverterInterface $ nameConverter = null )
46
+ public function __construct (TypeFactoryInterface $ typeFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , NameConverterInterface $ nameConverter = null , ResourceClassResolverInterface $ resourceClassResolver = null )
44
47
{
48
+ $ this ->typeFactory = $ typeFactory ;
45
49
$ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
46
50
$ this ->propertyNameCollectionFactory = $ propertyNameCollectionFactory ;
47
51
$ this ->propertyMetadataFactory = $ propertyMetadataFactory ;
48
52
$ this ->nameConverter = $ nameConverter ;
49
- $ this ->typeFactory = $ typeFactory ;
53
+ $ this ->resourceClassResolver = $ resourceClassResolver ;
50
54
}
51
55
52
56
/**
@@ -62,16 +66,20 @@ public function addDistinctFormat(string $format): void
62
66
/**
63
67
* {@inheritdoc}
64
68
*/
65
- public function buildSchema (string $ resourceClass , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?Schema $ schema = null , ?array $ serializerContext = null , bool $ forceCollection = false ): Schema
69
+ public function buildSchema (string $ className , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?Schema $ schema = null , ?array $ serializerContext = null , bool $ forceCollection = false ): Schema
66
70
{
67
71
$ schema = $ schema ?? new Schema ();
68
- if (null === $ metadata = $ this ->getMetadata ($ resourceClass , $ type , $ operationType , $ operationName , $ serializerContext )) {
72
+ if (null === $ metadata = $ this ->getMetadata ($ className , $ type , $ operationType , $ operationName , $ serializerContext )) {
69
73
return $ schema ;
70
74
}
71
75
[$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ metadata ;
72
76
77
+ if (null === $ resourceMetadata && (null !== $ operationType || null !== $ operationName )) {
78
+ throw new \LogicException ('The $operationType and $operationName arguments must be null for non-resource class. ' );
79
+ }
80
+
73
81
$ version = $ schema ->getVersion ();
74
- $ definitionName = $ this ->buildDefinitionName ($ resourceClass , $ format , $ type , $ operationType , $ operationName , $ serializerContext );
82
+ $ definitionName = $ this ->buildDefinitionName ($ className , $ format , $ type , $ operationType , $ operationName , $ serializerContext );
75
83
76
84
if (null === $ operationType || null === $ operationName ) {
77
85
$ method = Schema::TYPE_INPUT === $ type ? 'POST ' : 'GET ' ;
@@ -103,12 +111,13 @@ public function buildSchema(string $resourceClass, string $format = 'json', stri
103
111
104
112
$ definition = new \ArrayObject (['type ' => 'object ' ]);
105
113
$ definitions [$ definitionName ] = $ definition ;
106
- if (null !== $ description = $ resourceMetadata ->getDescription ()) {
114
+ if (null !== $ resourceMetadata && null !== $ description = $ resourceMetadata ->getDescription ()) {
107
115
$ definition ['description ' ] = $ description ;
108
116
}
109
117
// see https://github.com/json-schema-org/json-schema-spec/pull/737
110
118
if (
111
119
Schema::VERSION_SWAGGER !== $ version &&
120
+ null !== $ resourceMetadata &&
112
121
(
113
122
(null !== $ operationType && null !== $ operationName && null !== $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , 'deprecation_reason ' , null , true )) ||
114
123
null !== $ resourceMetadata ->getAttribute ('deprecation_reason ' , null )
@@ -118,7 +127,7 @@ public function buildSchema(string $resourceClass, string $format = 'json', stri
118
127
}
119
128
// externalDocs is an OpenAPI specific extension, but JSON Schema allows additional keys, so we always add it
120
129
// See https://json-schema.org/latest/json-schema-core.html#rfc.section.6.4
121
- if (null !== $ iri = $ resourceMetadata ->getIri ()) {
130
+ if (null !== $ resourceMetadata && null !== $ iri = $ resourceMetadata ->getIri ()) {
122
131
$ definition ['externalDocs ' ] = ['url ' => $ iri ];
123
132
}
124
133
@@ -200,12 +209,12 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
200
209
$ schema ->getDefinitions ()[$ definitionName ]['properties ' ][$ normalizedPropertyName ] = $ propertySchema ;
201
210
}
202
211
203
- private function buildDefinitionName (string $ resourceClass , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?array $ serializerContext = null ): string
212
+ private function buildDefinitionName (string $ className , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?array $ serializerContext = null ): string
204
213
{
205
- [$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ this ->getMetadata ($ resourceClass , $ type , $ operationType , $ operationName , $ serializerContext );
214
+ [$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ this ->getMetadata ($ className , $ type , $ operationType , $ operationName , $ serializerContext );
206
215
207
- $ prefix = $ resourceMetadata ->getShortName ();
208
- if (null !== $ inputOrOutputClass && $ resourceClass !== $ inputOrOutputClass ) {
216
+ $ prefix = $ resourceMetadata ? $ resourceMetadata -> getShortName () : ( new \ ReflectionClass ( $ className )) ->getShortName ();
217
+ if (null !== $ inputOrOutputClass && $ className !== $ inputOrOutputClass ) {
209
218
$ prefix .= ': ' .md5 ($ inputOrOutputClass );
210
219
}
211
220
@@ -224,14 +233,22 @@ private function buildDefinitionName(string $resourceClass, string $format = 'js
224
233
return $ name ;
225
234
}
226
235
227
- private function getMetadata (string $ resourceClass , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType , ?string $ operationName , ?array $ serializerContext ): ?array
236
+ private function getMetadata (string $ className , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType , ?string $ operationName , ?array $ serializerContext ): ?array
228
237
{
229
- $ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ resourceClass );
238
+ if (!$ this ->isResourceClass ($ className )) {
239
+ return [
240
+ null ,
241
+ $ serializerContext ,
242
+ $ className ,
243
+ ];
244
+ }
245
+
246
+ $ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ className );
230
247
$ attribute = Schema::TYPE_OUTPUT === $ type ? 'output ' : 'input ' ;
231
248
if (null === $ operationType || null === $ operationName ) {
232
- $ inputOrOutput = $ resourceMetadata ->getAttribute ($ attribute , ['class ' => $ resourceClass ]);
249
+ $ inputOrOutput = $ resourceMetadata ->getAttribute ($ attribute , ['class ' => $ className ]);
233
250
} else {
234
- $ inputOrOutput = $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , $ attribute , ['class ' => $ resourceClass ], true );
251
+ $ inputOrOutput = $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , $ attribute , ['class ' => $ className ], true );
235
252
}
236
253
237
254
if (null === ($ inputOrOutput ['class ' ] ?? null )) {
0 commit comments