Skip to content

Conversation

lyrixx
Copy link
Contributor

@lyrixx lyrixx commented Feb 21, 2025

with the following code and test, it crashed

<?php

namespace App;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\OpenApi\Model\Operation;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use PhpParser\Node\Attribute;
use PhpParser\Node\Expr\New_;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Rector\AbstractRector;

final class ApipMigrateOpenApiRector extends AbstractRector
{
    public function __construct(
        private readonly ValueResolver $valueResolver,
    ) {
    }

    public function getNodeTypes(): array
    {
        return [New_::class, Attribute::class];
    }

    public function refactor(Node $node)
    {
        if ($node instanceof New_ && is_a($this->getName($node->class), HttpOperation::class, true)) {
            return $this->convert($node);
        }

        if ($node instanceof Attribute && ApiProperty::class === $this->getName($node->name)) {
            return $this->convert($node);
        }

        return null;
    }

    private function convert(Node $node): ?Node
    {
        $scope = $node->getAttribute('scope');
        $update = false;

        foreach ($node->args as $arg) {
            if ('openapiContext' !== $this->getName($arg)) {
                continue;
            }


            $arg->name->name = 'openapi';

            $argValue = $arg->value;


            if (!$argValue instanceof Node\Expr\Array_) {
                // It should be an array! If it's not, we can't do anything.
                continue;
            }
            $update = true;

            $openApiArgs = [];

            foreach ($argValue->items as $item) {
                $openApiArgs[] = $i = new Node\Arg(
                    $item->value,
                    name: new Node\Identifier($this->valueResolver->getValue($item->key)),
                );
                $i->setAttribute('multiline', true);
                // Should we copy the current scope ? 
                // $i->setAttribute('scope', $scope);
            }

            $arg->value = new New_(
                new Node\Name\FullyQualified(Operation::class),
                $openApiArgs,
                [
                    'scope' => $scope,
                ]
            );

            return $node;
        }
 
        return null;
    }
}
<?php

namespace AppBundle\Api\Dto\AuditLog;

use ApiPlatform\Metadata\ApiProperty;
use AppBundle\Entity\Organization as OrganizationEntity;
use Symfony\Component\Serializer\Attribute\Groups;

#[Groups('auditLog/read')]
class Organization
{
    public function __construct(
        public ?OrganizationEntity $organization,
        public string $organizationName,
        public bool $loginPasswordAllowed,
        public bool $twoFactorRequired,
        #[ApiProperty(openapiContext: [
            'type' => 'object',
            'properties' => [
                'allowed' => ['type' => 'boolean'],
                'providers' => ['type' => 'object', 'additionalProperties' => ['type' => 'boolean']],
            ],
        ])]
        public ?array $oAuthRestriction = null,
    ) {
    }
}
-----
<?php

namespace AppBundle\Api\Dto\AuditLog;

use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\Metadata\ApiProperty;
use AppBundle\Entity\Organization as OrganizationEntity;
use Symfony\Component\Serializer\Attribute\Groups;

#[Groups('auditLog/read')]
class Organization
{
    public function __construct(
        public ?OrganizationEntity $organization,
        public string $organizationName,
        public bool $loginPasswordAllowed,
        public bool $twoFactorRequired,
        #[ApiProperty(openapi: new Operation(
            type: 'object',
            properties: [
                'allowed' => ['type' => 'boolean'],
                'providers' => ['type' => 'object', 'additionalProperties' => ['type' => 'boolean']],
            ],
        ))]
        public ?array $oAuthRestriction = null,
    ) {
    }
}

@samsonasik
Copy link
Member

Thank you, it is better to use the statement that the attribute belong instead of direct Attribute so you always got correct refresh scope, but this change is ok for me :)

@samsonasik samsonasik merged commit a8eabc6 into rectorphp:main Feb 21, 2025
44 checks passed
@lyrixx lyrixx deleted the refresh-attribute branch February 21, 2025 17:26
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.

2 participants