Skip to content

Allow generating documentation when property and method start from "is" #4064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 16, 2021
Merged

Allow generating documentation when property and method start from "is" #4064

merged 6 commits into from
Feb 16, 2021

Conversation

sidz
Copy link
Contributor

@sidz sidz commented Feb 16, 2021

Q A
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
License MIT

Currently API Platform doesn't generate documentation for case when both property and method names start from is. We need to define isIsActive or getIsActive or canIsActive or hasIsActive

For example:

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource(
 *     normalizationContext={"groups"={"read_user"}, "skip_null_values"=false},
 *     denormalizationContext={"groups"={"create_user"}},
 *     itemOperations={},
 *     collectionOperations={
 *         "get"= {
 *             "normalization_context"={"groups"={"read_user"}, "skip_null_values"=false},
 *         },
 *         "post"={
 *             "validation_groups"={"user:create"}
 *         }
 *     }
 * )
 *
 * @ORM\Entity
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer", options={"unsigned"=true})
     *
     * @Groups({"read_user"})
     */
    private ?int $id = null;

    /**
     * @ORM\Column(type="string", length=50)
     *
     * @Groups({"read_user", "create_user"})
     */
    private string $name;

    /**
     * @ORM\Column(type="boolean")
     *
     * @Groups({"read_user", "create_user"})
     */
    private bool $isActive;

    public function __construct(string $name, bool $isActive)
    {
        $this->name = $name;
        $this->isActive = $isActive;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }
    
    public function isActive(): bool
    {
        return $this->isActive;
    }
}

In this case documentation won't include property isActive. But if we add missed property to the request it will be processed successfully and entity will be created.

Documentation for such case is not generated due to changes in symfony since 5.1 inside ReflectionExtractor: https://github.com/symfony/symfony/blob/v5.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php#L232-L253

Let's see on line https://github.com/symfony/symfony/blob/v5.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php#L239-L240

Property $camelProp contains IsActive string which will be iteratively concatenated with all possible prefixes. We get 4 possible method names which we may have to solve this issue:

  • isIsActive
  • canIsActive
  • getIsActive
  • hasIsActive

But we don't have such method in our class and we would like to use isActive without prefixes.
So we go to line https://github.com/symfony/symfony/blob/v5.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php#L249

there we can see that $reflClass->hasMethod($getsetter) will find method isActive in our class but $allowGetterSetter is equal to false as $context['enable_getter_setter_extraction'] is null.

So this PR solve this issue and it will be possible to have isActive property and the same method name.

p.s. The answer on question why everything works fine when we add missed isActive property to the request manually is on lines: https://github.com/symfony/property-access/blob/v5.1.0/PropertyAccessor.php#L472-L476

@@ -327,6 +327,9 @@ private function getFactoryOptions(array $serializerContext, array $validationGr
{
$options = [];

/* @see https://github.com/symfony/symfony/blob/v5.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php */
$options['enable_getter_setter_extraction'] = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO you could add it directly above 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I thought about this as well.
But IMHO it won't look good in case when it will be needed to add one more key-value there with explanation comment.

Copy link
Member

@alanpoulain alanpoulain Feb 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not like this:

$options = [
    /* @see https://github.com/symfony/symfony/blob/v5.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php */
    'enable_getter_setter_extraction' => true,
    // Another explanation.
    'another_option' => 'foo',
];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay to do this if you still think that it is critical. Should I?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not critical at all. It's only some code style 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in this case 👍

phpunit.xml.dist Outdated
@@ -6,6 +6,7 @@
backupGlobals="false"
bootstrap="tests/Fixtures/app/bootstrap.php"
colors="true"
stopOnFailure="true"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it wanted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope. reverted

@alanpoulain
Copy link
Member

Couldn't it be like a BC? Since the generated OpenAPI documentation will change after this PR?
Shouldn't it be better to target main?

@sidz
Copy link
Contributor Author

sidz commented Feb 16, 2021

@alanpoulain I'd say that it is not a BC as API Platform just doesn't generate right documentation.

without this fix and exactly same structure like in my example class I'd be able to add isActive property manually click on Execute button and user will be created/updated.

@alanpoulain
Copy link
Member

Makes sense indeed.

@alanpoulain alanpoulain merged commit 2c31621 into api-platform:2.6 Feb 16, 2021
@alanpoulain
Copy link
Member

Thank you @sidz and congratulations for your first contribution (it seems)!

@sidz
Copy link
Contributor Author

sidz commented Feb 16, 2021

thank you @alanpoulain, @dunglas for assistance

@sidz sidz deleted the pass-enable-getter-setter-extractor branch February 16, 2021 14:19
{
$this->isDummyBoolean = $isDummyBoolean;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing existing tests is not recommended, I hope that using isDummyBoolean on the property still works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isDummyBoolean method was not taken for the schema generation, only the property.
I don't think the tests have changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants