-
Notifications
You must be signed in to change notification settings - Fork 107
[16.0] [IMP] shopinvader anonymous partner promotion #1570
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
base: 16.0
Are you sure you want to change the base?
[16.0] [IMP] shopinvader anonymous partner promotion #1570
Conversation
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.
See my comment.
Otherwise thanks a lot for you good code ! The promotion implementation is super cool !
That's a big scary change :) To help with the review, could you describe why this change is necessary? What is the use case that was not satisfied by the current implementation?
This raises a warning in my head, as a GET should not normally not have side effects. |
cc/ @qgroulard |
Of course, the main use case is this akretion#16: providing a way for different auth mechanisms to have the same behavior when an anonymous partner becomes a real partner (here called partner promotion) mainly resulting in a cart transfer. I strongly think this shouldn't be handled by a module named As for the GET side effect I generally agree with this principle, as this does not get along really well with cache, but here the implementation is not GET specific and is triggered as soon two different partners kind are detected in the current auth dependency and as far as I can see the anonymous partner creation is currently also done here (
|
from odoo.addons.shopinvader_anonymous_partner.models.res_partner import COOKIE_NAME | ||
|
||
|
||
class PartnerPromotionCase(TransactionCase): |
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.
Please use BaseCommon
class to disable mail tracking
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.
wdym?
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.
There is a BaseCommon class that extend the TransactionCase and pass the right context params to speed the test
https://github.com/odoo/odoo/blob/166a55f159dca1fba29fe9b1aed2a923994a6672/odoo/addons/base/tests/common.py#L19
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.
@paradoxxxzero I think we should refactor step by step.
I strongly think this shouldn't be handled by a module named shopinvader_api_signin_jwt as it is cart specific but not jwt specific.
I tend to agree with that. But when we designed shopinvader_api_signin_jwt
with @qgroulard our reasoning was that if anyone needs that jwt signin route without the cart, a refactoring could be done later.
Your idea of some kind of _promote
method sounds good. So maybe we should start with that refactoring. I mean doing a PR that factors out these 4 lines, and nothing else, and see where these leads us.
anonymous_cart = env["sale.order"].sudo()._find_open_cart(anonymous_partner.id)
if anonymous_cart:
anonymous_cart._transfer_cart(partner.id)
anonymous_cart.unlink()
I also think these promote methods should be totally ignorant of any authentication stuff, so the part that returns a flag about the cookie does not belong there.
67e7319
to
180a9a9
Compare
…er method on res.partner This method is used when an anonymous partner logs in.
…n dependencies When a logged partner and anonymous partner coexist
… partner As this will be done in shopinvader_sale_cart_anonymous_partner. This also allows to remove the shopinvader_sale_cart dependency in a signin module
…anonymous_partner And always drop the cookie
180a9a9
to
6a30a66
Compare
eb422e2
to
98f70b6
Compare
Alright I removed the changes to the dependencies and called directly For reference (@sebastienbeau) I also updated the |
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.
A couple more comment, but I like the direction it takes.
anonymous_partner = self._get_anonymous_partner__cookie(cookies) | ||
if anonymous_partner: | ||
partner._promote_from_anonymous_partner(anonymous_partner) | ||
self._delete_anonymous_partner__cookie(cookies, response) |
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 I would leave this logic in shopinvader_api_signin_jwt
because it is tricky and so closely related to the authentication method.
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 are in the shopinvader_anonymous_partner
module here which role is to identify an anonymous partner with a cookie, unless I am missing something it seems the right place to delete the cookie. IMO if we sought a more generic approach, we should have an implementation agnostic interface and no __cookie
methods.
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.
Maybe, but I don't think we should encourage re-use or override of that part. It is only 4 lines and quite delicate, so I feel it's better to copy it (probably with subtle changes) in other signin methods.
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.
That's why the _promote_from_anonymous_partner
method exists, that's the one that should be inherited to implement a promotion mechanism.
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.
That's why the
_promote_from_anonymous_partner
method exists, that's the one that should be inherited to implement a promotion mechanism.
Yes that part is ok. It's specifically this _promote_anonymous_partner
method that I'm not comfortable with.
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 I know, I just don't understand why :). If you override the _promote_anonymous_partner
in the shopinvader_anonymous_partner
you can only mean to change the promotion mechanism of anonymous partners and in this case yeah you can break the anonymous partner flow.
I am certainly missing something here, but I expect a shopinvader_anonymous_partner
module to handle the flow of anonymous partners regardless of how the identified partner authentication is implemented.
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.
just don't understand why
It makes the logic of the signin route much harder to follow, for no real benefit.
anonymous_cart._transfer_cart(self.id) | ||
anonymous_cart.unlink() | ||
|
||
return rv |
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.
👍
anonymous_cart.unlink() | ||
env["res.partner"]._delete_anonymous_partner__cookie(request.cookies, response) | ||
|
||
env["res.partner"]._promote_anonymous_partner(partner, request.cookies, response) |
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.
env["res.partner"]._promote_anonymous_partner(partner, request.cookies, response) | |
if anonymous_partner: | |
partner._promote_from_anonymous_partner(anonymous_partner) | |
env["res.partner"]._delete_anonymous_partner__cookie(request.cookies, response) |
I find this much easier to follow and don't see the benefit of factoring it out.
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.
After discussing with @paradoxxxzero
I understand why he do not want to duplicate the following code
anonymous_partner = self._get_anonymous_partner__cookie(cookies)
if anonymous_partner:
partner._promote_from_anonymous_partner(anonymous_partner)
env["res.partner"]._delete_anonymous_partner__cookie(cookies, response)
Indeed right now the promote is be done
- on signin in jwt auth
- on signin in partner_auth
- on reset the password in partner auth
In long term we may have additionnal way to signin
- like magic link
- other auth implementation (oauth, social network ...).
So everythere we do the promote we have to put the previous 4 lines
So it make sense to call a method that always do the same thing.
At the same time
I understand @sbidoul.
The first time, I look at the code I was a little lost with the method "_promote_anonymous_partner" and "_promote_from_anonymous_partner"
It hard to understand what they do without looking deeper in the code
Also it always fell weird for me to have cookie logic in the "res.partner" model
Maybe we should
move the logic of cookies stuff in a dedicated helper
class ShopinvaderAnonymousCookieHelper(models.AbstractModel):
_name = "shopinvader_anonymous_partner.cookie.helper"
def _create_anonymous_partner__cookie(self, response: Response):
...
def _delete_anonymous_partner__cookie(self, cookies: Cookies, response: Response):
...
def _create_anonymous_partner__cookie(self, response: Response):
...
def _promote_anonymous_partner_and_delete_cookie(self, partner: ResPartner, cookies: Cookies, response: Response):
anonymous_partner = self._get_anonymous_partner__cookie(cookies)
if anonymous_partner:
partner._promote_from_anonymous_partner(anonymous_partner)
self._delete_anonymous_partner__cookie(cookies, response)
Then on the partner we only have code related to the model, that act on model
class ResPartner
def _create_anonymous_partner__token(self):
....
def _get_anonymous_partner__token(self, token: str):
....
def _promote_from_anonymous_partner(self, anonymous_partner: ResPartner):
...
Then in jwt signin we just do and in all other method that do an auth
helper = env["shopinvader_anonymous_partner.cookie.helper"]
helper._promote_anonymous_partner_and_delete_cookie(partner, request.cookies, response)
What do you think ?
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.
It's hard to name thing 😢
This PR removes the cart transfer from
shopinvader_api_signin_jwt
(and itsshopinvader_sale_cart
dependency) and instead create a new_promote_anonymous_partner
function that is now called byshopinvader_fastapi_auth_*
modules when the two partner coexist.A new
shopinvader_sale_cart_anonymous_partner
glue module handles the cart transfer on promotion.This slightly changes the time when the cart is transfered from the login to the next request (which should be the cart GET anyway)
FYI @sebastienbeau