From 8586a1e12a2b4235eabd3fdb0b90ce9b1212fd18 Mon Sep 17 00:00:00 2001 From: Dominic Luidold Date: Tue, 7 Jan 2025 11:36:56 +0100 Subject: [PATCH] feat: Allow ignoring properties from schema --- src/Attribute/Ignore.php | 19 +++++++++++ .../Annotations/AnnotationsReader.php | 19 +++++++++++ src/ModelDescriber/JMSModelDescriber.php | 6 ++++ src/ModelDescriber/ObjectModelDescriber.php | 4 +++ .../Functional/Controller/ApiController81.php | 13 ++++++++ .../Functional/Controller/JMSController81.php | 12 +++++++ .../Entity/EntityWithIgnoredProperty.php | 26 +++++++++++++++ .../Functional/Entity/JMSIgnoredProperty.php | 33 +++++++++++++++++++ tests/Functional/FunctionalTest.php | 14 ++++++++ tests/Functional/JMSFunctionalTest.php | 13 ++++++++ 10 files changed, 159 insertions(+) create mode 100644 src/Attribute/Ignore.php create mode 100644 tests/Functional/Entity/EntityWithIgnoredProperty.php create mode 100644 tests/Functional/Entity/JMSIgnoredProperty.php diff --git a/src/Attribute/Ignore.php b/src/Attribute/Ignore.php new file mode 100644 index 000000000..2fbaba73d --- /dev/null +++ b/src/Attribute/Ignore.php @@ -0,0 +1,19 @@ +symfonyConstraintAnnotationReader->updateProperty($reflection, $property, $serializationGroups); } + /** + * @param \ReflectionProperty[]|\ReflectionMethod[] $reflections + */ + public function shouldDescribeProperty(array $reflections): bool + { + if (\PHP_VERSION_ID < 80100) { + return true; + } + + foreach ($reflections as $reflection) { + if ([] !== $reflection->getAttributes(Ignore::class)) { + return false; + } + } + + return true; + } + /** * Whether the model describer should continue reading class properties * after updating the open api schema from an `OA\Schema` definition. diff --git a/src/ModelDescriber/JMSModelDescriber.php b/src/ModelDescriber/JMSModelDescriber.php index 9520bc3a7..a0f6eed50 100644 --- a/src/ModelDescriber/JMSModelDescriber.php +++ b/src/ModelDescriber/JMSModelDescriber.php @@ -159,6 +159,12 @@ public function describe(Model $model, OA\Schema $schema) } } + if (!$annotationsReader->shouldDescribeProperty($reflections)) { + $context->popPropertyMetadata(); + + continue; + } + $groups = $this->computeGroups($context, $item->type); if (true === $item->inline && isset($item->type['name'])) { diff --git a/src/ModelDescriber/ObjectModelDescriber.php b/src/ModelDescriber/ObjectModelDescriber.php index 6e486c729..d3006fa5c 100644 --- a/src/ModelDescriber/ObjectModelDescriber.php +++ b/src/ModelDescriber/ObjectModelDescriber.php @@ -130,6 +130,10 @@ public function describe(Model $model, OA\Schema $schema) $reflections = $this->getReflections($reflClass, $propertyName); + if (!$annotationsReader->shouldDescribeProperty($reflections)) { + continue; + } + // Check if a custom name is set foreach ($reflections as $reflection) { $serializedName = $annotationsReader->getPropertyName($reflection, $serializedName); diff --git a/tests/Functional/Controller/ApiController81.php b/tests/Functional/Controller/ApiController81.php index 66ada15d4..da5b8b1af 100644 --- a/tests/Functional/Controller/ApiController81.php +++ b/tests/Functional/Controller/ApiController81.php @@ -24,6 +24,7 @@ use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityThroughNameConverter; use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithAlternateType81; use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithFalsyDefaults; +use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithIgnoredProperty; use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithNullableSchemaSet; use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithObjectType; use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithRef; @@ -350,6 +351,18 @@ public function entityWithUuid() { } + #[Route('/entity-with-ignored-property', methods: ['GET', 'POST'])] + #[OA\Response( + response: 200, + description: 'success', + content: new OA\JsonContent( + ref: new Model(type: EntityWithIgnoredProperty::class), + ), + )] + public function entityWithIgnoredProperty() + { + } + #[Route('/form-with-alternate-type', methods: ['POST'])] #[OA\Response( response: 204, diff --git a/tests/Functional/Controller/JMSController81.php b/tests/Functional/Controller/JMSController81.php index cc3c40d00..b9bbaa2fa 100644 --- a/tests/Functional/Controller/JMSController81.php +++ b/tests/Functional/Controller/JMSController81.php @@ -17,6 +17,7 @@ use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex81; use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSDualComplex; use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSEnum81; +use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSIgnoredProperty; use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSNamingStrategyConstraints; use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSUser; use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSChat; @@ -157,4 +158,15 @@ public function discriminatorMapAction() public function enumArrayAction() { } + + #[Route('/api/jms_ignored_property', methods: ['GET'])] + #[OA\Response( + response: 200, + description: 'Success', + content: new Model(type: JMSIgnoredProperty::class) + ) + ] + public function ignoredProperty() + { + } } diff --git a/tests/Functional/Entity/EntityWithIgnoredProperty.php b/tests/Functional/Entity/EntityWithIgnoredProperty.php new file mode 100644 index 000000000..09bdf4f38 --- /dev/null +++ b/tests/Functional/Entity/EntityWithIgnoredProperty.php @@ -0,0 +1,26 @@ +getModel('EntityWithUuid')->toJson(), true)); } + public function testEntityWithIgnoredProperty(): void + { + self::assertEquals([ + 'schema' => 'EntityWithIgnoredProperty', + 'type' => 'object', + 'required' => ['regularProperty'], + 'properties' => [ + 'regularProperty' => [ + 'type' => 'string', + ], + ], + ], json_decode($this->getModel('EntityWithIgnoredProperty')->toJson(), true)); + } + public function testEntitiesWithRefInSchemaDoNoReadOtherProperties(): void { $model = $this->getModel('EntityWithRef'); diff --git a/tests/Functional/JMSFunctionalTest.php b/tests/Functional/JMSFunctionalTest.php index c0dc3a774..dfeecd64e 100644 --- a/tests/Functional/JMSFunctionalTest.php +++ b/tests/Functional/JMSFunctionalTest.php @@ -461,6 +461,19 @@ public function testModeDiscriminatorMap(): void ], json_decode($this->getModel('JMSAbstractUser')->toJson(), true)); } + public function testIgnoredProperty(): void + { + self::assertEquals([ + 'schema' => 'JMSIgnoredProperty', + 'type' => 'object', + 'properties' => [ + 'regular_property' => [ + 'type' => 'string', + ], + ], + ], json_decode($this->getModel('JMSIgnoredProperty')->toJson(), true)); + } + /** * @param array $options */