-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] availability to add serializer config at transport level #44951
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
[Messenger] availability to add serializer config at transport level #44951
Conversation
<framework:transport name="foo" dsn="null://"> | ||
<framework:symfony_serializer format="xml"> | ||
<framework:context> | ||
<framework:some_other_context>true</framework:some_other_context> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests are failing here
1) Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\XmlFrameworkExtensionTest::testMessengerWithTransportSerializerConfig
Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: Unable to parse file "/home/nikophil/works/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transport_serializer_config.xml": [ERROR 1871] Element '{http://symfony.com/schema/dic/symfony}symfony_serializer': This element is not expected. Expected is one of ( {http://symfony.com/schema/dic/symfony}options, {http://symfony.com/schema/dic/symfony}retry-strategy ). (in /home/nikophil/works/symfony/ - line 18, column 0)
don't know how to make them pass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema needs an update
symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
Line 499 in 6b9fafb
<xsd:element name="symfony-serializer" type="messenger_symfony_serializer" minOccurs="0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok thanks, I was thinking it was something automatically generated
/** | ||
* @author Nicolas PHILIPPE <nikophil@gmail.com> | ||
*/ | ||
interface FormatAndContextAwareSerializerInterface extends SerializerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've introduced this new interface to not break BC
public function __construct(SymfonySerializerInterface $serializer = null, string $format = 'json', array $context = []) | ||
{ | ||
$this->serializer = $serializer ?? self::create()->serializer; | ||
$this->format = $format; | ||
$this->context = $context + [self::MESSENGER_SERIALIZATION_CONTEXT => true]; | ||
$this->setFormat($format); | ||
$this->setContext($context); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think removing $format
and $context
from constructor would have been a BC break, that's why I let them here. Maybe we should introduce some deprecation?
$container->setDefinition($serializerWithCustomConfigurationId = "messenger.transport.{$name}.serializer", new ChildDefinition($serializerId)) | ||
->addMethodCall('setFormat', [$transport['symfony_serializer']['format'] ?? $config['serializer']['symfony_serializer']['format']]) | ||
->addMethodCall('setContext', [($transport['symfony_serializer']['context'] ?? []) + $config['serializer']['symfony_serializer']['context']]) | ||
; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new definition(s) needed here: if we want different config with the same service (for example default symfony's serializer) it has to be duplicated.
if (isset($transport['symfony_serializer'])) { | ||
$serializerDefinition = $container->findDefinition($serializerId); | ||
|
||
if (!isset(class_implements($serializerDefinition->getClass())[FormatAndContextAwareSerializerInterface::class])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it's safer to rely on an interface to inject these parameters instead of constructor's parameters order: the service could be defined in user land
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this interface makes sense in the Messenger package. It's way too much tied to the Symfony Serializer implementation, and it's not even used at runtime, only here for DI.
the service could be defined in user land
If the user defines the service in userland, then they can inject the format and context themselves and won't need this configuration entry, right?
I'd remove the interface, trait and method call to inject format and context using constructor's parameters, as for the messenger.symfony_serializer
section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user defines the service in userland, then they can inject the format and context themselves and won't need this configuration entry, right?
it depends: presently there is no way to define a custom serializer in framework.messenger.serializer.default_serializer
which could serialize in both json and xml (except by adding some plumbing of course)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @ogizanagi here
Hey! I think @monteiro has recently worked with this code. Maybe they can help review this? Cheers! Carsonbot |
Hey! I think @alirezamirsepassi has recently worked with this code. Maybe they can help review this? Cheers! Carsonbot |
@@ -1386,6 +1386,18 @@ function ($a) { | |||
->children() | |||
->scalarNode('dsn')->end() | |||
->scalarNode('serializer')->defaultNull()->info('Service id of a custom serializer to use.')->end() | |||
->arrayNode('symfony_serializer') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about a less specific context
node, eg.context.format
to be used by SF serializer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean we could have this kind of configuration?
messenger:
transports:
foo:
context:
format: xml
xml_root_node_name: foo
and then $context['format']
would be passsed to $this->serializer->(de)serialize()
in Symfony\Component\Messenger\Transport\Serialization\SerializerInterface
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, but it was it bit pre-mature of me. I think messenger.transports.N.serializer
is what couples transports with a serializer, and this new config complexifies things for little gain.
In that sense, i prefer to add a new prototyped messenger.serializers
and deprecate messenger.serializer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not configure a dedicated serializer instead?
edit; i see, it's not prototyped :( to me that would be more flexible. And then there's no need for a new "format & context aware" abstraction.
then on a per-transport base, maybe configuring default stamps is better (eg. SerializerStamp)
hi @ro0NL
what do you mean by "prototyped"? |
see https://symfony.com/doc/current/components/config/definition.html#array-nodes instead of a single symfony_serializer under note you can also use any service ID of yours at i guess ideally we'd allow to configure many serializer instances directly under |
OK thanks for explanation, indeed why not defining several serializers under
yep, but in my experience, all these "custom" transport serializers tend to always be used to vary format and context (and to chose in which POPO message class it should deserialize incoming message if it comes from outside of the current app)
why not, but in our case, it would be only useful for the context, and not to change the format. And by the way it would require some extra changes in messenger's configuration since |
👍 let's not couple |
That was not my intention, just saying it was not covering the whole scope of this PR |
I like the idea, in theory, of re-using
In other words, the "messenger serializer" is a different thing that the "Symfony serializer" (though sometimes we use Symfony's serializer inside of a messenger serializer). So, I like this PR on a high level. The Messenger serializer that leverages the Symfony serializer - https://github.com/symfony/symfony/blob/6.1/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php - already is able to handle a custom "format" and "context". So this PR just allows you to specify that at a transport-level as well. What I'm less sure about is exactly how the configuration should look. It does feel a bit weird to have a Cheers! |
Note that we just deprecated |
Hello,
this PR adds the possibility to override default format and context for messenger's serializer:
I hope this does not introduce BC breaks - tried to avoid it