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

Dehorusify ProfileController #2274

Merged
merged 18 commits into from Jun 3, 2015

Conversation

Projects
None yet
4 participants
@nickstenning
Contributor

nickstenning commented Jun 3, 2015

As a first step towards remove horus completely from our accounts system, here's the ProfileController reworked to not depend on horus. Along the way I've fixed the following bugs:

  • logout not working on master (also fixed temporarily by #2270)
  • update anyone's subscriptions simply by picking a subscription id

The tests are now an order of magnitude easier to write.

nickstenning added some commits Jun 2, 2015

Move profile email confirmation validator into schema
This is where the validator belongs, as it is part of the overall form
schema.
Rename EditProfileSchema -> ProfileSchema
This is the schema for any and all ProfileController form submissions,
not just edit_profile.
Simplify validation of forms in ProfileController
Rather than using exceptions to signal failure to validate a form, just
interrogate the return value of the validate_form helper function.

Also simplifies testing.
Use User model directly
Working towards getting ProfileController independent of horus means
removing our reliance on horus's instance variables such as `self.User`.
Remove ProfileController dependency on horus
Integration with horus is causing us endless pain, both in testing and
in extending the behaviour of account endpoints. This commit removes the
ProfileController's dependency on horus.
Remove class wrappers around tests
We don't use setUp and tearDown functions, so I don't think there's much
value in using class-based test suites.
Don't explicitly specify error code 400
This is the default when a view returns errors through the
AsyncFormViewMapper.
Don't return JSON with HTTP redirects
This reverts a change made in 39d1468 which assumes that a view returns
JSON along with an HTTPRedirection response. This breaks logout (and
other views) that return empty redirects.

In addition, because the edit_profile view function no longer returns a
redirect, the AsyncFormViewMapper no longer needs to preserve JSON
responses in this fashion.
@landscape-bot

This comment has been minimized.

Show comment
Hide comment
@landscape-bot

landscape-bot Jun 3, 2015

Code Health
Repository health decreased by 0.78% when pulling f6f04a1 on dehorusify-profilecontroller into 06278fb on master.

landscape-bot commented Jun 3, 2015

Code Health
Repository health decreased by 0.78% when pulling f6f04a1 on dehorusify-profilecontroller into 06278fb on master.

@@ -64,6 +60,16 @@ def ajax_form(request, result):
return result
def validate_form(form, data):
"""Validate POST payload data for a form."""
try:

This comment has been minimized.

@seanh

seanh Jun 3, 2015

Contributor

Nice

@seanh

seanh Jun 3, 2015

Contributor

Nice

@view_auth_defaults
@view_config(attr='edit_profile', route_name='edit_profile')
@view_config(attr='disable_user', route_name='disable_user')
@view_config(attr='profile', route_name='profile')
class ProfileController(horus.views.ProfileController):
class ProfileController(object):

This comment has been minimized.

@seanh

seanh Jun 3, 2015

Contributor

Yes :) I love classes that inherit from just object

@seanh

seanh Jun 3, 2015

Contributor

Yes :) I love classes that inherit from just object

This comment has been minimized.

@tilgovi

tilgovi Jun 3, 2015

Contributor

👍

@tilgovi

tilgovi Jun 3, 2015

Contributor

👍

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them - they're all in this class. Otherwise it can be difficult, when dealing with large test modules. Agree on not using setup and teardown or self though.

Contributor

seanh commented on f5cc263 Jun 3, 2015

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them - they're all in this class. Otherwise it can be difficult, when dealing with large test modules. Agree on not using setup and teardown or self though.

@nickstenning

This comment has been minimized.

Show comment
Hide comment
@nickstenning

nickstenning Jun 3, 2015

Contributor

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them

I get that, but don't you achieve the same thing by:

  • putting them all in the same place
  • giving them good names? (i.e. test_edit_profile_does_foo, test_edit_profile_does_bar)
Contributor

nickstenning commented Jun 3, 2015

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them

I get that, but don't you achieve the same thing by:

  • putting them all in the same place
  • giving them good names? (i.e. test_edit_profile_does_foo, test_edit_profile_does_bar)
@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

edit_profile() couild do with being broken up into multiple methods, but it was already as long before this pr

Contributor

seanh commented Jun 3, 2015

edit_profile() couild do with being broken up into multiple methods, but it was already as long before this pr

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

This all looks good to me, code is much simpler and easier to understand.

I'm a little disturbed by how long edit_profile() is and how few tests we have for edit_profile() and ProfileSchema, that isn't this PR's fault, but it might be good to prioritise writing more tests for those.

Contributor

seanh commented Jun 3, 2015

This all looks good to me, code is much simpler and easier to understand.

I'm a little disturbed by how long edit_profile() is and how few tests we have for edit_profile() and ProfileSchema, that isn't this PR's fault, but it might be good to prioritise writing more tests for those.

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them
I get that, but don't you achieve the same thing by:

putting them all in the same place
giving them good names? (i.e. test_edit_profile_does_foo, test_edit_profile_does_bar)

Sort of. But it's the difference between scrolling through a long tests file and noticing "Hey, the function names are starting with test_edit_profile_ now" versus seeing a class with a docstring that says "This is where all the tests for edit_profile() go", which I think is clearer. The indentation difference and ability to search for "class " makes it easier to jump around in and get an overview of the file.

I'm happy enough either way though, in past projects I've tried to insist on not having test classes so that people wouldn't be tempted to start using self.

Contributor

seanh commented Jun 3, 2015

For what it's worth the value in wrapping the tests for a given function in a class is making it easier to find the tests for that function and know that you've found all of them
I get that, but don't you achieve the same thing by:

putting them all in the same place
giving them good names? (i.e. test_edit_profile_does_foo, test_edit_profile_does_bar)

Sort of. But it's the difference between scrolling through a long tests file and noticing "Hey, the function names are starting with test_edit_profile_ now" versus seeing a class with a docstring that says "This is where all the tests for edit_profile() go", which I think is clearer. The indentation difference and ability to search for "class " makes it easier to jump around in and get an overview of the file.

I'm happy enough either way though, in past projects I've tried to insist on not having test classes so that people wouldn't be tempted to start using self.

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

I think some of landscape's complaints about the tests are actually valid: unused variables, unused imports, == True, line too long. But not all the missing docstring and other docstring complaints, not the redefined name or the unused arguments. And I'm not sure if the valid complaints are really introduced by this pr.

Contributor

seanh commented Jun 3, 2015

I think some of landscape's complaints about the tests are actually valid: unused variables, unused imports, == True, line too long. But not all the missing docstring and other docstring complaints, not the redefined name or the unused arguments. And I'm not sure if the valid complaints are really introduced by this pr.

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

I'm happy with this, recommend @tilgovi has a quick look as I think he understands horus, colander and deform better than I do.

Contributor

seanh commented Jun 3, 2015

I'm happy with this, recommend @tilgovi has a quick look as I think he understands horus, colander and deform better than I do.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

I'd say if test files are getting so long that it becomes a pain to identify and find a subset of them that you need it might mean the tested module has grown similarly too large and needs to be broken up. I'm not familiar with the pain @seanh is suggestion, but let's watch for it to occur.

Contributor

tilgovi commented Jun 3, 2015

I'd say if test files are getting so long that it becomes a pain to identify and find a subset of them that you need it might mean the tested module has grown similarly too large and needs to be broken up. I'm not familiar with the pain @seanh is suggestion, but let's watch for it to occur.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

I'm general a fan of fewer classes in most things, so I'm tempted to agree with @nickstenning here for now.

Contributor

tilgovi commented Jun 3, 2015

I'm general a fan of fewer classes in most things, so I'm tempted to agree with @nickstenning here for now.

except _InvalidEditProfileRequestError as err:
return dict(errors=err.errors)
"""Handle POST payload from profile update form."""
if self.request.method != 'POST':

This comment has been minimized.

@tilgovi

tilgovi Jun 3, 2015

Contributor

This can also be done by adding the request_method predicate to the view decorator, but this is okay.

@tilgovi

tilgovi Jun 3, 2015

Contributor

This can also be done by adding the request_method predicate to the view decorator, but this is okay.

return dict(errors=e.error.children)
password = appstruct.get('password')
if password:
user.password = password

This comment has been minimized.

@tilgovi

tilgovi Jun 3, 2015

Contributor

I think we're forgetting to add the updated user to the db session so it commits at the end of the request.

@tilgovi

tilgovi Jun 3, 2015

Contributor

I think we're forgetting to add the updated user to the db session so it commits at the end of the request.

This comment has been minimized.

@nickstenning

nickstenning Jun 3, 2015

Contributor

You don't need to add objects that came from the database in the first place. sqlalchemy does dirty checking. You only need to add new objects.

@nickstenning

nickstenning Jun 3, 2015

Contributor

You don't need to add objects that came from the database in the first place. sqlalchemy does dirty checking. You only need to add new objects.

This comment has been minimized.

@nickstenning
@nickstenning
if user:
# TODO: maybe have an explicit disabled flag in the status
user.password = self.User.generate_random_password()
self.db.add(user)

This comment has been minimized.

@tilgovi

tilgovi Jun 3, 2015

Contributor

I think this needs to be here.

@tilgovi

tilgovi Jun 3, 2015

Contributor

I think this needs to be here.

@@ -367,7 +345,6 @@ def unsubscribe(self):
subscription = Subscriptions.get_by_id(request, subscription_id)
if subscription:
subscription.active = False
self.db.add(subscription)

This comment has been minimized.

@tilgovi

tilgovi Jun 3, 2015

Contributor

This, too.

@tilgovi

tilgovi Jun 3, 2015

Contributor

This, too.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

Totally owe you 🍻 for this one. Makes me so happy.

Only thing missing is actually adding the changes to the db :).

Contributor

tilgovi commented Jun 3, 2015

Totally owe you 🍻 for this one. Makes me so happy.

Only thing missing is actually adding the changes to the db :).

@nickstenning

This comment has been minimized.

Show comment
Hide comment
@nickstenning

nickstenning Jun 3, 2015

Contributor

I'll just take a quick look through some of the things Sean raised, so don't merge it juuust yet.

Contributor

nickstenning commented Jun 3, 2015

I'll just take a quick look through some of the things Sean raised, so don't merge it juuust yet.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

Okay. I've also just checked this locally and was able to change my password, so I'm confused about SQLAlchemy sessions and the need for an explicit add. If you can clarify why it's not needed that would help me.

Contributor

tilgovi commented Jun 3, 2015

Okay. I've also just checked this locally and was able to change my password, so I'm confused about SQLAlchemy sessions and the need for an explicit add. If you can clarify why it's not needed that would help me.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

http://docs.sqlalchemy.org/en/latest/orm/session_state_management.html#quickie-intro-to-object-states

Right. I was mistaken because I'd seen excessive use of add in other code, I think.

If the object came from the DB it's already persistent and its attributes are managed so when the transaction commits at the end of the request it's done.

Contributor

tilgovi commented Jun 3, 2015

http://docs.sqlalchemy.org/en/latest/orm/session_state_management.html#quickie-intro-to-object-states

Right. I was mistaken because I'd seen excessive use of add in other code, I think.

If the object came from the DB it's already persistent and its attributes are managed so when the transaction commits at the end of the request it's done.

@nickstenning

This comment has been minimized.

Show comment
Hide comment
@nickstenning

nickstenning Jun 3, 2015

Contributor

Oh, sorry, I did already link to some sqla docs in the line comment above.

Contributor

nickstenning commented Jun 3, 2015

Oh, sorry, I did already link to some sqla docs in the line comment above.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

No worries.

Contributor

tilgovi commented Jun 3, 2015

No worries.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

LGTM. Whenever @seanh is happy.

Contributor

tilgovi commented Jun 3, 2015

LGTM. Whenever @seanh is happy.

@seanh

This comment has been minimized.

Show comment
Hide comment
@seanh

seanh Jun 3, 2015

Contributor

LGTM, I'll leave it up to Nick to correct as many of my points as he wants, none of them were blockers

Contributor

seanh commented Jun 3, 2015

LGTM, I'll leave it up to Nick to correct as many of my points as he wants, none of them were blockers

Minor cleanups in h.accounts.test.views_test
Remove some unused imports, fix some long lines and unused variables,
etc.
@nickstenning

This comment has been minimized.

Show comment
Hide comment
@nickstenning

nickstenning Jun 3, 2015

Contributor

Ready for merge when the lights go green.

Contributor

nickstenning commented Jun 3, 2015

Ready for merge when the lights go green.

@landscape-bot

This comment has been minimized.

Show comment
Hide comment
@landscape-bot

landscape-bot Jun 3, 2015

Code Health
Repository health decreased by 0.32% when pulling 4beb481 on dehorusify-profilecontroller into 06278fb on master.

landscape-bot commented Jun 3, 2015

Code Health
Repository health decreased by 0.32% when pulling 4beb481 on dehorusify-profilecontroller into 06278fb on master.

@tilgovi

This comment has been minimized.

Show comment
Hide comment
@tilgovi

tilgovi Jun 3, 2015

Contributor

Super good.

Contributor

tilgovi commented on 4beb481 Jun 3, 2015

Super good.

tilgovi added a commit that referenced this pull request Jun 3, 2015

@tilgovi tilgovi merged commit c47131f into master Jun 3, 2015

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details

@tilgovi tilgovi deleted the dehorusify-profilecontroller branch Jun 3, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment