Skip to content

Commit 1f84724

Browse files
authored
Merge pull request #2788 from pskt/swagger-skip-properties
Skip not readable and not writable properties in Swagger
2 parents 62834f5 + 9af83e3 commit 1f84724

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed

src/Swagger/Serializer/DocumentationNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,10 @@ private function getDefinitionSchema(bool $v3, string $resourceClass, ResourceMe
595595
$options = isset($serializerContext[AbstractNormalizer::GROUPS]) ? ['serializer_groups' => $serializerContext[AbstractNormalizer::GROUPS]] : [];
596596
foreach ($this->propertyNameCollectionFactory->create($resourceClass, $options) as $propertyName) {
597597
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
598+
if (!$propertyMetadata->isReadable() && !$propertyMetadata->isWritable()) {
599+
continue;
600+
}
601+
598602
$normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName, $resourceClass, self::FORMAT, $serializerContext ?? []) : $propertyName;
599603
if ($propertyMetadata->isRequired()) {
600604
$definitionSchema['required'][] = $normalizedPropertyName;

tests/Swagger/Serializer/DocumentationNormalizerV2Test.php

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,182 @@ public function testNormalizeWithNormalizationAndDenormalizationGroups()
12261226
$this->assertEquals($expected, $normalizer->normalize($documentation));
12271227
}
12281228

1229+
public function testNormalizeSkipsNotReadableAndNotWritableProperties()
1230+
{
1231+
$documentation = new Documentation(new ResourceNameCollection([Dummy::class]), 'Test API', 'This is a test API.', '1.2.3', ['jsonld' => ['application/ld+json']]);
1232+
1233+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
1234+
$propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->shouldBeCalled()->willReturn(new PropertyNameCollection(['id', 'dummy', 'name']));
1235+
1236+
$dummyMetadata = new ResourceMetadata('Dummy', 'This is a dummy.', 'http://schema.example.com/Dummy', ['get' => ['method' => 'GET', 'status' => '202'], 'put' => ['method' => 'PUT', 'status' => '202']], ['get' => ['method' => 'GET', 'status' => '202'], 'post' => ['method' => 'POST', 'status' => '202']], ['pagination_client_items_per_page' => true]);
1237+
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
1238+
$resourceMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn($dummyMetadata);
1239+
1240+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
1241+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'id')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT), null, false, false));
1242+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'dummy')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'This is a public id.', true, false, true, true, false, true, null, null, []));
1243+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'This is a name.', true, true, true, true, false, false, null, null, []));
1244+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
1245+
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
1246+
1247+
$operationMethodResolverProphecy = $this->prophesize(OperationMethodResolverInterface::class);
1248+
$operationMethodResolverProphecy->getItemOperationMethod(Dummy::class, 'get')->shouldBeCalled()->willReturn('GET');
1249+
$operationMethodResolverProphecy->getItemOperationMethod(Dummy::class, 'put')->shouldBeCalled()->willReturn('PUT');
1250+
$operationMethodResolverProphecy->getCollectionOperationMethod(Dummy::class, 'get')->shouldBeCalled()->willReturn('GET');
1251+
$operationMethodResolverProphecy->getCollectionOperationMethod(Dummy::class, 'post')->shouldBeCalled()->willReturn('POST');
1252+
1253+
$operationPathResolver = new CustomOperationPathResolver(new OperationPathResolver(new UnderscorePathSegmentNameGenerator()));
1254+
1255+
$normalizer = new DocumentationNormalizer(
1256+
$resourceMetadataFactoryProphecy->reveal(),
1257+
$propertyNameCollectionFactoryProphecy->reveal(),
1258+
$propertyMetadataFactoryProphecy->reveal(),
1259+
$resourceClassResolverProphecy->reveal(),
1260+
$operationMethodResolverProphecy->reveal(),
1261+
$operationPathResolver
1262+
);
1263+
1264+
$expected = [
1265+
'swagger' => '2.0',
1266+
'basePath' => '/app_dev.php/',
1267+
'info' => [
1268+
'title' => 'Test API',
1269+
'description' => 'This is a test API.',
1270+
'version' => '1.2.3',
1271+
],
1272+
'paths' => new \ArrayObject([
1273+
'/dummies' => [
1274+
'get' => new \ArrayObject([
1275+
'tags' => ['Dummy'],
1276+
'operationId' => 'getDummyCollection',
1277+
'produces' => ['application/ld+json'],
1278+
'summary' => 'Retrieves the collection of Dummy resources.',
1279+
'parameters' => [
1280+
[
1281+
'name' => 'page',
1282+
'in' => 'query',
1283+
'required' => false,
1284+
'type' => 'integer',
1285+
'description' => 'The collection page number',
1286+
],
1287+
[
1288+
'name' => 'itemsPerPage',
1289+
'in' => 'query',
1290+
'required' => false,
1291+
'type' => 'integer',
1292+
'description' => 'The number of items per page',
1293+
],
1294+
],
1295+
'responses' => [
1296+
202 => [
1297+
'description' => 'Dummy collection response',
1298+
'schema' => [
1299+
'type' => 'array',
1300+
'items' => ['$ref' => '#/definitions/Dummy'],
1301+
],
1302+
],
1303+
],
1304+
]),
1305+
'post' => new \ArrayObject([
1306+
'tags' => ['Dummy'],
1307+
'operationId' => 'postDummyCollection',
1308+
'consumes' => ['application/ld+json'],
1309+
'produces' => ['application/ld+json'],
1310+
'summary' => 'Creates a Dummy resource.',
1311+
'parameters' => [
1312+
[
1313+
'name' => 'dummy',
1314+
'in' => 'body',
1315+
'description' => 'The new Dummy resource',
1316+
'schema' => ['$ref' => '#/definitions/Dummy'],
1317+
],
1318+
],
1319+
'responses' => [
1320+
202 => [
1321+
'description' => 'Dummy resource created',
1322+
'schema' => ['$ref' => '#/definitions/Dummy'],
1323+
],
1324+
400 => ['description' => 'Invalid input'],
1325+
404 => ['description' => 'Resource not found'],
1326+
],
1327+
]),
1328+
],
1329+
'/dummies/{id}' => [
1330+
'get' => new \ArrayObject([
1331+
'tags' => ['Dummy'],
1332+
'operationId' => 'getDummyItem',
1333+
'produces' => ['application/ld+json'],
1334+
'summary' => 'Retrieves a Dummy resource.',
1335+
'parameters' => [
1336+
[
1337+
'name' => 'id',
1338+
'in' => 'path',
1339+
'type' => 'string',
1340+
'required' => true,
1341+
],
1342+
],
1343+
'responses' => [
1344+
202 => [
1345+
'description' => 'Dummy resource response',
1346+
'schema' => ['$ref' => '#/definitions/Dummy'],
1347+
],
1348+
404 => ['description' => 'Resource not found'],
1349+
],
1350+
]),
1351+
'put' => new \ArrayObject([
1352+
'tags' => ['Dummy'],
1353+
'operationId' => 'putDummyItem',
1354+
'consumes' => ['application/ld+json'],
1355+
'produces' => ['application/ld+json'],
1356+
'summary' => 'Replaces the Dummy resource.',
1357+
'parameters' => [
1358+
[
1359+
'name' => 'id',
1360+
'in' => 'path',
1361+
'type' => 'string',
1362+
'required' => true,
1363+
],
1364+
[
1365+
'name' => 'dummy',
1366+
'in' => 'body',
1367+
'description' => 'The updated Dummy resource',
1368+
'schema' => ['$ref' => '#/definitions/Dummy'],
1369+
],
1370+
],
1371+
'responses' => [
1372+
202 => [
1373+
'description' => 'Dummy resource updated',
1374+
'schema' => ['$ref' => '#/definitions/Dummy'],
1375+
],
1376+
400 => ['description' => 'Invalid input'],
1377+
404 => ['description' => 'Resource not found'],
1378+
],
1379+
]),
1380+
],
1381+
]),
1382+
'definitions' => new \ArrayObject([
1383+
'Dummy' => new \ArrayObject([
1384+
'type' => 'object',
1385+
'description' => 'This is a dummy.',
1386+
'externalDocs' => ['url' => 'http://schema.example.com/Dummy'],
1387+
'properties' => [
1388+
'dummy' => new \ArrayObject([
1389+
'type' => 'string',
1390+
'description' => 'This is a public id.',
1391+
'readOnly' => true,
1392+
]),
1393+
'name' => new \ArrayObject([
1394+
'type' => 'string',
1395+
'description' => 'This is a name.',
1396+
]),
1397+
],
1398+
]),
1399+
]),
1400+
];
1401+
1402+
$this->assertEquals($expected, $normalizer->normalize($documentation, DocumentationNormalizer::FORMAT, ['base_url' => '/app_dev.php/']));
1403+
}
1404+
12291405
public function testFilters()
12301406
{
12311407
$filterLocatorProphecy = $this->prophesize(ContainerInterface::class);

0 commit comments

Comments
 (0)