Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[Messenger] Added a middleware that validates messages#26648
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
| <serviceid="messenger.middleware.validator"class="Symfony\Component\Messenger\Middleware\ValidatingMiddleware"> | ||
| <argumenttype="service"id="validator" /> | ||
| <tagname="message_bus_middleware"priority="8" /> |
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.
Any reasoning for such priority? I'd argue it should be higher by default as it would be one of the most important "first steps".
| * @author Magnus Nordlander <magnus@fervo.se> | ||
| * @author Tobias Nyholm <tobias.nyholm@gmail.com> | ||
| */ | ||
| class ValidatingMiddlewareimplements MiddlewareInterface |
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.
ValidationMiddleware feels better to me and is more coherent with the other middleware names 🤔 (exceptLoggingMiddleware 🙊)
| } | ||
| } | ||
| $next($message); |
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.
Don't forget thereturn here.
| */ | ||
| class ValidatingMiddlewareimplements MiddlewareInterface | ||
| { | ||
| protected$validator; |
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 would this one beprotected? I'd close first if we can soprivate, nah?
sroze commentedMar 24, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
The only remaining question is "Should this validation be enabled by default" (i.e. shouldn't we drop the To me, it sounds like we should enable it by default because:
What do you think? /cc @symfony/deciders |
Nyholm commentedMar 26, 2018
I've squashed my commits and added validation by default. Validating a message without validation metadata will always pass. |
| parent::__construct($this->__toString()); | ||
| } | ||
| publicfunction__toString() |
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.
AFAIK, we never defined__toString method on exceptions in the core. Not sure this is desired now.
| return$this->violatingMessage; | ||
| } | ||
| publicfunctiongetViolations() |
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.
Missing return type?
| protected$violations; | ||
| protected$violatingMessage; | ||
| publicfunction__construct($violatingMessage,ConstraintViolationListInterface$violations) |
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.
Missing a@param object $violatingMessage docblock?
| class ValidationMiddlewareimplements MiddlewareInterface | ||
| { | ||
| /** | ||
| * @var ValidatorInterface |
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.
Can be removed as there is already a typehint on the constructor
Nyholm commentedMar 27, 2018
Thank you for the review. I've updated accordingly. I've also rebased the PR on master. |
| $this->violatingMessage =$violatingMessage; | ||
| $this->violations =$violations; | ||
| parent::__construct($this->__toString()); |
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.
- parent::__construct($this->__toString());+ parent::__construct(sprintf("Message of type %s failed validation", get_class($this->violatingMessage));
as the__toString method is now removed?
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.
Oh, Thanks
sroze left a comment
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.
Almost there, thanks for the work.
I slept on it and I believe we need an opt-out. It doesn't need to be complicated: a boolean atframework.messenger.validation would do it. Could you add that?
| /** | ||
| * @param object $violatingMessage | ||
| * @param ConstraintViolationListInterface $violations |
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.
We don't need this one.
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.
Okey. Thanks
Nyholm commentedMar 27, 2018
I agree. But there will be plenty of merge conflicts. I will add "opt-out" config once#26647 is merged. |
bac60a9 toab6c055CompareNyholm commentedMar 27, 2018
I've rebased my PR and added a way to opt-out from message validation |
| ->end() | ||
| ->end() | ||
| ->arrayNode('validation') | ||
| ->canBeDisabled() |
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.
- ->canBeDisabled()+ ->{!class_exists(FullStack::class) && class_exists(Validation::class) ? 'canBeDisabled' : 'canBeEnabled'}()
so it does not appear as enabled when the validator component isn't installed?
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.
Excellent idea. I was looking for something like this. And if someone tries to use validation without the validation component I'll throw an exception.
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.
Indeed, we use to throw such exceptions from the FrameworkExtension :)
| class ValidationFailedExceptionextends \RuntimeExceptionimplements ExceptionInterface | ||
| { | ||
| protected$violations; | ||
| protected$violatingMessage; |
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 no inheritance use case for these properties in core then they should be private
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.
Thank you
| */ | ||
| publicfunction__construct($violatingMessage,ConstraintViolationListInterface$violations) | ||
| { | ||
| $this->violatingMessage =$violatingMessage; |
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.
Is it really needed? I'm used to handle one command at a time so I always have the message at hand, am I missing a use case? The existingNoHandlerForMessageException doesn't expose it
ogizanagiMar 29, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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 difference is that theNoHandlerForMessageException is a logic exception (or at least, should be to me), it is meant to fail badly. No need to access the exception programmatically and retrieve the issuing message.
ValidationFailedException is a runtime exception, meant to be caught and handled/transformed to a better representation (e.g: an API problem response). It can also be used in a profiler panel or whatever :)
Anyway, even if we often handle one message at the time, use-cases with multiple messages do exist. We should cover them. 👍
| $this->violatingMessage =$violatingMessage; | ||
| $this->violations =$violations; | ||
| parent::__construct(sprintf('Message of type "%s" failed validation',get_class($this->violatingMessage))); |
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.
Missing dot at the end 😉
Nyholm commentedMar 29, 2018
Thank you for the reviews. I've updated the PR accordingly |
…ception (ogizanagi)This PR was merged into the 4.1-dev branch.Discussion----------[Messenger] Make NoHandlerForMessageException a logic exception| Q | A| ------------- | ---| Branch? | master <!-- see below -->| Bug fix? | no| New feature? | no <!-- don't forget to update src/**/CHANGELOG.md files -->| BC breaks? | no <!-- seehttps://symfony.com/bc -->| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->| Tests pass? | yes <!-- please add some, will be required by reviewers -->| Fixed tickets |symfony/symfony#26648 (comment) <!-- #-prefixed issue number(s), if any -->| License | MIT| Doc PR | N/A <!-- required for new features -->To me, a missing handler for a message is a program logic exception (or misconfiguration). Even if it's only detected at runtime here.It's the same as `ServiceNotFoundException` which even is an `\InvalidArgumentException`. Could eventually also be the case here.Commits-------0489bbd948 [Messenger] Make NoHandlerForMessageException a logic exception
…ception (ogizanagi)This PR was merged into the 4.1-dev branch.Discussion----------[Messenger] Make NoHandlerForMessageException a logic exception| Q | A| ------------- | ---| Branch? | master <!-- see below -->| Bug fix? | no| New feature? | no <!-- don't forget to update src/**/CHANGELOG.md files -->| BC breaks? | no <!-- seehttps://symfony.com/bc -->| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->| Tests pass? | yes <!-- please add some, will be required by reviewers -->| Fixed tickets |#26648 (comment) <!-- #-prefixed issue number(s), if any -->| License | MIT| Doc PR | N/A <!-- required for new features -->To me, a missing handler for a message is a program logic exception (or misconfiguration). Even if it's only detected at runtime here.It's the same as `ServiceNotFoundException` which even is an `\InvalidArgumentException`. Could eventually also be the case here.Commits-------0489bbd [Messenger] Make NoHandlerForMessageException a logic exception
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.
Can you add some tests? That it adds (and not if disabled) the middleware.
| if ($config['middlewares']['validation']['enabled']) { | ||
| if (!$container->has('validator')) { | ||
| thrownewLogicException('The Validation middleware is only available when the validator component is installed and enabled. Try running "composer require symfony/validator".'); |
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.
- validator component+ Validator component
Nyholm commentedApr 3, 2018
I've fixed the typo and added tests. I've also rebased on master. The Travis tests failure seams unrelated. |
sroze commentedApr 3, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@fabpot as you disagreed with the Doctrine one... WDYT about this one? 🤔 |
fabpot commentedApr 3, 2018
This one is fine as this is about integrating various Symfony components. |
sroze left a comment
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.
@Nyholm can you squash and rebase?
fabpot commentedApr 3, 2018
@sroze no need to rebase (as there are no merge commits) and no need to rebase (as there are no conflicts). Our gh tools does that automatically :) |
sroze commentedApr 3, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@fabpot that rebase request was meant so that we would have green ticks when I |
sroze commentedApr 3, 2018
Thank you@Nyholm. |
… (Nyholm)This PR was squashed before being merged into the 4.1-dev branch (closes#26648).Discussion----------[Messenger] Added a middleware that validates messages| Q | A| ------------- | ---| Branch? | master| Bug fix? | no| New feature? | yes| BC breaks? | no| Deprecations? | no| Tests pass? | yes| Fixed tickets || License | MIT| Doc PR | comingThis is inspired by [Fervo](https://github.com/fervo/ValidatedMessage). It runs the validator over messages implementing the `ValidatedMessageInterface`.Commits-------43a5171 [Messenger] Added a middleware that validates messages
sroze commentedApr 3, 2018
One of the failing tests was actually related. Fixed in#26782. |
Nyholm commentedApr 4, 2018
Thank you for merging |
Uh oh!
There was an error while loading.Please reload this page.
This is inspired byFervo. It runs the validator over messages implementing the
ValidatedMessageInterface.