Skip to content

Implement STI on User model #64959

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

Merged
merged 15 commits into from
Apr 17, 2025
Merged

Implement STI on User model #64959

merged 15 commits into from
Apr 17, 2025

Conversation

nicklathe
Copy link
Contributor

@nicklathe nicklathe commented Apr 1, 2025

This PR adds STI (Single Table Inheritance) to our User model. There are two new classes introduced, Teacher and Student that both inherit from the User base class. One benefit of implementing STI on the User model is that it allows us organize the model along the axis of user_type and move student and teacher specific logic into their respective classes.

This functionality should not break existing usage. For example, you can continue to create a new user by calling User.create(user_type: "Teacher"). You can also now instead call Teacher.create(), as STI provides additional class methods.

This PR adds the following changes:

  • Creates teacher.rb and student.rb and associated classes
  • Adds STI inheritance column override (default is type, this overrides that to be user_type
  • Adds find_sti_class method override
  • Adds logic to set the user class in user_builder.rb
  • Updates unit tests to handle the introduction of the new STI classes

Links

Testing story

You can test locally, and will be able to create new teacher and student accounts as before, as well as downgrade/upgrade account styles (Teacher -> Student, Student -> Teacher).

Deployment strategy

Follow-up work

Privacy

Security

Caching

PR Checklist:

  • Tests provide adequate coverage
  • Privacy and Security impacts have been assessed
  • Code is well-commented
  • New features are translatable or updates will not break translations
  • Relevant documentation has been added or updated
  • User impact is well-understood and desirable
  • Pull Request is labeled appropriately
  • Follow-up work items (including potential tech debt) are tracked and linked

assert_select '#user_age'
assert_select '#user_us_state', false
assert_select '#user_gender_student_input', false
assert_select '#student_age'
Copy link
Contributor Author

@nicklathe nicklathe Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rails prepends the user class for these attributes because the form builder knows the object is a User model. However since a user is now either a Teacher or Student class, these attributes are now being prepended with student_ rather than user_. This is probably obvious to those very familiar with Rails, but I was unaware of this prepending logic.

  - Add STI definitions to user.rb
  - Add Student and Teacher classes
  - Fix unit tests in user_test.rb

Signed-off-by: Nick Lathe <[email protected]>
  - Partial registration user builder
  - Password resetter by email
@nicklathe nicklathe force-pushed the nicklathe/user-sti-v2 branch from 8d26809 to 6fdd7c1 Compare April 7, 2025 14:58
@nicklathe nicklathe marked this pull request as ready for review April 10, 2025 02:58
@nicklathe nicklathe requested a review from a team April 10, 2025 23:16
  - Move class mapping to find_sti_class method
  - Simplify factory
TYPE_TO_STI_CLASS_MAP = {
TYPE_TEACHER => ::Teacher,
TYPE_STUDENT => ::Student,
'staff' => ::Teacher # Powerschool sends through 'staff' instead of 'teacher'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know if Powerschool SSO is still in use?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, I was just maintaining current functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that feels like something we should do in a followup PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it can wait for sure. I'm running a few queries to see if we can remove this later.

@carl-codeorg
Copy link
Contributor

Successfully tested locally using a variety of users. For Canvas, though, if I launch as a teacher for the first time, I get an email validation error on the finish signup screen. Doesn't happen on latest staging. Are you able to repro this?

Screenshot 2025-04-15 at 1 43 52 PM

@nicklathe
Copy link
Contributor Author

nicklathe commented Apr 15, 2025

Successfully tested locally using a variety of users. For Canvas, though, if I launch as a teacher for the first time, I get an email validation error on the finish signup screen. Doesn't happen on latest staging. Are you able to repro this?

Interesting. I'm working through some failing unit tests with my most recent PR feedback changes pushed. Will try to repro and validate this new bug. Thanks for testing and discovering this!

Comment on lines 13 to 16
user = ::User.new_with_session(user_params, session)

sti_class = ::User.find_sti_class(user_params[:user_type])
user = user.becomes!(sti_class) if sti_class
Copy link
Member

@artem-vavilov artem-vavilov Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User#become! reinitializes the user instance, which breaks the tricky argument setup done by User.new_with_session:

Suggested change
user = ::User.new_with_session(user_params, session)
sti_class = ::User.find_sti_class(user_params[:user_type])
user = user.becomes!(sti_class) if sti_class
params = user_params
user_class = ::User.find_sti_class(params[:user_type]) || ::User
user = user_class.new_with_session(params, session)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, this is the fix! Thanks @artem-vavilov!

@@ -32,6 +35,7 @@ def call
user_params[:school_info_attributes].permit(:school_id, :school_name, :school_type, :zip, :country)
end
when ::User::TYPE_STUDENT
# binding.pry
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# binding.pry

Copy link
Contributor

@carl-codeorg carl-codeorg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LTI teacher signup working for me now. Nice job on the quick fix.

Copy link
Member

@artem-vavilov artem-vavilov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicklathe Before merging this PR, please let me double-check the places where we manually convert the user instance into the required STI class, as it should work automatically

@artem-vavilov artem-vavilov mentioned this pull request Apr 16, 2025
@nicklathe
Copy link
Contributor Author

@nicklathe Before merging this PR, please let me double-check the places where we manually convert the user instance into the required STI class, as it should work automatically

@artem-vavilov I just approved your linked PR to merge into this branch.

Copy link
Member

@artem-vavilov artem-vavilov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! 🙌🏻

@nicklathe nicklathe merged commit 0735a8b into staging Apr 17, 2025
6 checks passed
@nicklathe nicklathe deleted the nicklathe/user-sti-v2 branch April 17, 2025 17:34
nicklathe added a commit that referenced this pull request Apr 18, 2025
@nicklathe nicklathe restored the nicklathe/user-sti-v2 branch April 23, 2025 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants