Skip to content
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

[Refactor][ApiBundle] Unify section resolver usage #16097

Open
6 tasks
diimpp opened this issue Apr 5, 2024 · 1 comment
Open
6 tasks

[Refactor][ApiBundle] Unify section resolver usage #16097

diimpp opened this issue Apr 5, 2024 · 1 comment
Labels
API APIs related issues and PRs.

Comments

@diimpp
Copy link
Member

diimpp commented Apr 5, 2024

1

Currently ApiBundle uses mix of techniques to determine current api section:

  • Uri based check
  • Route name based check
  • Currently logged in user based check
Examples

private function isNotAdminApiSection(): bool
{
return !$this->uriBasedSectionContext->getSection() instanceof AdminApiSection;
}

private function isShopOperation(array $context): bool
{
if (isset($context['item_operation_name'])) {
return \str_starts_with($context['item_operation_name'], 'shop_get');
}
if (isset($context['collection_operation_name'])) {
return \str_starts_with($context['collection_operation_name'], 'shop_get');
}
return false;

if ($user instanceof AdminUserInterface && in_array('ROLE_API_ACCESS', $user->getRoles(), true)) {
return $this->productRepository->findOneByCode($id);
}
Assert::keyExists($context, ContextKeys::CHANNEL);
/** @var ChannelInterface $channel */
$channel = $context[ContextKeys::CHANNEL];
return $this->productRepository->findOneByChannelAndCodeWithAvailableAssociations($channel, $id);

2

Additionally some places use ROLE_API_ACCESS check to distinguish between admin user and admin user in api context, which imo should be removed and left to firewall to handle or otherwise role name should be parameterized or moved to checker class, which can be overridden easily.

if ($user instanceof AdminUserInterface && in_array('ROLE_API_ACCESS', $user->getRoles(), true)) {
return $this->productRepository->findOneByCode($id);
}

3

Another case is that serialization groups like admin:product:read were split into admin:product:index and admin:product:show recently, but usage for them is not updated, so normalizer like this

$data['variants'] = $object
->getEnabledVariants()
->map(fn (ProductVariantInterface $variant): string => $this->iriConverter->getIriFromResource($variant))
->getValues()
;
$defaultVariant = $this->defaultProductVariantResolver->getVariant($object);
$data['defaultVariant'] = $defaultVariant === null ? null : $this->iriConverter->getIriFromResource($defaultVariant);

is specific to admin:product:show, but will be executed at index as well.

Solution is to apply serialization normalizer based on serialization group, which can look like,

    public function __construct(
        private SectionProviderInterface $uriBasedSectionProvider,
        private array $serializationGroups,
    ) {
    }

    public function supportsNormalization($data, $format = null, $context = []): bool
    {
        if (isset($context[self::ALREADY_CALLED])) {
            return false;
        }

        return $data instanceof ProductInterface
            && $this->doSerializationGroupsMatch($context)
            && $this->uriBasedSectionProvider->getSection() instanceof AdminApiSection;
    }

    private function doSerializationGroupsMatch(array $context): bool
    {
        $groups = $context['groups'] ?? [];
        if (is_string($groups)) {
            $groups = [$groups];
        }

        return (bool) array_intersect($this->serializationGroups, $groups);
    }

Proposal:

  • Remove ROLE_API_ACCESS check entirely.
  • Use section resolver to determine current section.
    • If existing main request based section resolver has some performance penalty, consider APIP context uri section resolver as alternative.
    • If collection/item operation check still needed, make APIP route name checker to determine current operation type, e.g. collection or item (Though operation type should be specified on context directly without need to parse route naming) for places where both section + operation type should be used.
  • Apply serialization normalizer based on serialization group
  • Add section to namespace at DataProvider/Serializer/etc, where classes are section specific, example:
--- Sylius\Bundle\ApiBundle\Serializer\ProductNormalizer
+++ Sylius\Bundle\ApiBundle\Serializer\ShopApi\ProductNormalizer
@diimpp diimpp added the API APIs related issues and PRs. label Apr 5, 2024
@Rafikooo
Copy link
Contributor

Hello @diimpp, your proposal is valid. However, due to conflicting priorities, we will consider implementing the solution for your proposal in the Sylius 2.0 release. 🕺

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API APIs related issues and PRs.
Projects
None yet
Development

No branches or pull requests

2 participants