Skip to content
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

Add GroupMembership.roles column to model #9029

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Add GroupMembership.roles column to model #9029

wants to merge 1 commit into from

Conversation

seanh
Copy link
Contributor

@seanh seanh commented Oct 18, 2024

No description provided.

@seanh seanh requested a review from marcospri October 18, 2024 13:28
Comment on lines +37 to +43
class GroupMembershipRoles(enum.StrEnum):
"""The valid role strings that're allowed in the GroupMembership.roles column."""

MEMBER = "member"
MODERATOR = "moderator"
ADMIN = "admin"
OWNER = "owner"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pulled these four strings out into an enum because they'll inevitably need to be referenced from elsewhere in the code later

Comment on lines +62 to +67
sa.CheckConstraint(
" OR ".join(
f"""(roles = '["{role}"]'::jsonb)""" for role in GroupMembershipRoles
),
name="validate_role_strings",
),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This both ensures that roles has length 1 and that the one string is "member", "moderator", "admin" or "owner".

This'll be validated at the API level as well, but I like having the constraint at the DB level so that no one accessing the DB directly, or Python code other than the API, or a migration, etc can enter an invalid value.

Doing this as an SQL-level constraint is slightly inconvenient because if we want to widen the constraint in future we'll have to do a migration. But it'll be an easy migration, and this ensures that nothing can insert an invalid value (not even some non-ORM code e.g. in a migration or something).

In future if we want to allow LMS to insert custom roles the constraint will have to ignore all "{authority}:{role}"-formatted roles and consider only the core "{role}" ones, and enforce that there must be exactly one of those and it must be "member", "moderator", "admin" or "owner". That might be difficult to write as an SQL expression, so at that point perhaps we'll just remove this constraint and replace it with an SQLAlchemy @validates method (though I think those only work for code that's using the ORM).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

P.S. I'm not sure if SQLAlchemy has any other way to do an enum for the strings inside of a JSON array, I think probably not.

Copy link
Member

Choose a reason for hiding this comment

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

In future if we want to allow LMS to insert custom roles the constraint will have to ignore all

Maybe we are overthinking this, we could use another array "custom_roles" or similar.

@seanh seanh marked this pull request as ready for review October 18, 2024 13:55
Copy link
Member

@marcospri marcospri left a comment

Choose a reason for hiding this comment

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

Looks good. Note that this PR needs to be deployed after the migration is applied.

Comment on lines +62 to +67
sa.CheckConstraint(
" OR ".join(
f"""(roles = '["{role}"]'::jsonb)""" for role in GroupMembershipRoles
),
name="validate_role_strings",
),
Copy link
Member

Choose a reason for hiding this comment

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

In future if we want to allow LMS to insert custom roles the constraint will have to ignore all

Maybe we are overthinking this, we could use another array "custom_roles" or similar.

JSONB,
sa.CheckConstraint(
" OR ".join(
f"""(roles = '["{role}"]'::jsonb)""" for role in GroupMembershipRoles
Copy link
Member

Choose a reason for hiding this comment

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

I think using a loop here is not the best choice, it gives the impression that things will just work but they won't.

Listing them all will make it more obvious that a migration is needed as the model definition of the constraint needed a change.

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.

2 participants