-
Notifications
You must be signed in to change notification settings - Fork 438
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
fix: refactor api_new_meeting_registration. Fixes #7608 #7724
Conversation
rpcross
commented
Jul 21, 2024
- change to use JSON payload
- handle multiple records in one request
- change to use JSON payload - handle multiple records in one request
I am working on tests for this. Using a JSON payload means the existing require_api_key decorator won't work. Assuming JSON payload is the way to go, are there other precedents for checking API key? Function can do it's own validation, or can update decorator. |
datatracker/ietf/api/ietf_utils.py Lines 27 to 46 in b5ab4b6
|
@rpcross This may have fallen into the cracks because of 120? |
@rpcross - lets discuss this soon? |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7724 +/- ##
==========================================
+ Coverage 88.71% 88.78% +0.06%
==========================================
Files 310 312 +2
Lines 40882 40945 +63
==========================================
+ Hits 36270 36351 +81
+ Misses 4612 4594 -18 ☔ View full report in Codecov by Sentry. |
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 are a few comments inline, but I have reservations about the strategy used for removing/updating/replacing MeetingRegistration
records. As it's done, I'm pretty sure there are race conditions that can lead to corrupt registrations. E.g., if two identical requests to register a user are at nearly the same time, two identical registrations may be created. This will happen if the later-running request handler executes line 334 (regs = MeetingRegistration.objects.filter(meeting__number=number, email=email)
) before the earlier-running handler executes line 357 (new_reg.save()
).
At minimum, we ought to put a uniqueness constraint on MeetingRegistration
to guard against this. Not sure, but I think it'd need to be over email
, meeting
, and reg_type
.
I think that constraint should be enough to avoid data corruption, but it would result in some API errors that were unnecessary. I.e., the two simultaneous identical requests would both "succeed" in that their work would be done as requested, but the later one would get back an error. It'd be very cool to rework the API using get_or_create
(plus probably some additional subtlety handling) to avoid this, but I'm not sure it's necessary.
# get person | ||
person = Person.objects.filter(email__address=email).first() | ||
if not person: | ||
log.log(f"api_new_meeting_registration_v2 no Person found for {email}") |
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.
Do you want the registration system to know that the registration it just sent doesn't match a person? Think about whether telling the person that just registered "Oh no - something went wrong with your registration" might move some of the person-fixup work we do at meetings to the period before the meeting.
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 left this in for completeness, but do we expect this to happen? Since Registration now requires the participant to be logged in via the Datatracker, the existence of Person should be largely assured. My feeling is that all auth issues at the last few meetings have been either 1) someone is trying to access meetecho without having registered, 2) duplicate person issues.
# get existing records if any | ||
regs = MeetingRegistration.objects.filter(meeting__number=number, email=email) | ||
pks = [r.pk for r in regs] | ||
|
||
for registration in payload: | ||
new_reg = MeetingRegistration( | ||
meeting=meeting, | ||
first_name=registration['first_name'], | ||
last_name=registration['last_name'], | ||
affiliation=registration['affiliation'], | ||
country_code=registration['country_code'], | ||
person=person, | ||
email=email, | ||
reg_type=registration['reg_type'], | ||
ticket_type=registration['ticket_type'], | ||
checkedin=registration['checkedin']) | ||
|
||
# update any existing records if there are any | ||
new_reg.pk = _safe_pop(pks) | ||
if new_reg.pk: | ||
log.log(f"Updating MeetingRegistration record for meeting:{meeting} email:{email}") | ||
else: | ||
log.log(f"New MeetingRegistration record for meeting:{meeting} email:{email}") | ||
new_reg.save() |
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.
This confuses me, and looks error-prone - it could effectively swap pks on records depending on the order L334 returns objects. Why isn't this using get_or_create with defaults and using the created flag instead?
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 there a reason to prefer the current model where a single person's registration is represented as several MeetingRegistration
instances? The gymnastics in checking that the payload pertains only to a single email seems to be begging for a model that represents that as a single registration per email with fields representing the different options.
I think that'll greatly simplify the API implementation. It'll mean a migration, but I don't think that'll be terribly difficult.
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.
Also, as far as I can tell there are currently no models with foreign keys into MeetingRegistration
so it's not an going to break things now if PKs get swapped, but it'll do something in between cramping our style and causing serious bugs later.
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.
This confuses me, and looks error-prone - it could effectively swap pks on records depending on the order L334 returns objects. Why isn't this using get_or_create with defaults and using the created flag instead?
It certainly could swap pks. It is a brute force approach I chose for it's definitiveness. We can’t use get_or_create because there may be more than one existing registration record even for the same reg_type (two onsite one-day regs for example) AND the reg_type can change, switch from onsite to remote, so we’d be updating a record with a different reg_type. The simplest approach is to delete and re-create, or fully overwrite as I have done. Other approaches require more complexity. The following might work. Assuming that any notification will include all existing registrations for a given participant, and that a single registration save triggers the notification than it should be true that only one registration is added or was changed. If payload == 1 we can use get_or_create. (Except if payload == 1 and 2 already exist it means we missed a cancel somewhere). If payload > 1 we know that only one is new or updated and the rest should match existing records. We can do the work to determine which record doesn't match existing. This is a more complex procedure but would result in preserving the record PKs.
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 there a reason to prefer the current model where a single person's registration is represented as several
MeetingRegistration
instances? The gymnastics in checking that the payload pertains only to a single email seems to be begging for a model that represents that as a single registration per email with fields representing the different options.I think that'll greatly simplify the API implementation. It'll mean a migration, but I don't think that'll be terribly difficult.
We actually started with one MeetingRegistration record per person and had a reg_type field that was text and contained comma separated types, "onsite, hackathon_onsite", which proved problematic to deal with so we change the model to match registration, which could be multiple registration records per person. The current model seems appropriate.
<- meeting 120 onsite week pass
person A <- meeting 121 onsite one day pass
<- meeting 121 remote week pass
Or maybe breakout another table?
person A <- meeting 121 <- onsite one-day
<- remote week pass
# delete any remaining records | ||
if pks: | ||
MeetingRegistration.objects.filter(pk__in=pks).delete() |
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 L333-L356 change, this will have to be adjusted to match
# handle nomcom volunteer | ||
if registration['is_nomcom_volunteer'] and person: | ||
try: | ||
nomcom = NomCom.objects.get(is_accepting_volunteers=True) |
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.
Does the registration system check to see if a nomcom is accepting volunteers before offering the choice to someone?
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 does not. It's another level of integration that could be added. My understanding was that AMS would coordinate with the nomcom chair. When prepping to open registration if nomcom is accepting volunteers I would ensure the volunteer question appears on the reg form.
Update: class MeetingRegistrationDetail(models.Model): # name? And use a custom object manager like this: class MeetingRegistrationManager(models.Manager): And queries like this: We could combine reg_type and ticket_type, but the most frequent queries are those for specific reg_type “onsite” or “remote” so it seems reasonable to keep them separate. Are there better names for new classes, fields above? Considerations: For simplicity is it acceptable to clear and recreate the related objects on update? The above implementation is most flexible. Allowing the capture of all registration types currently represented, and new ones with the addition of new name types. Would a simpler approach of using boolean fields has_onsite, has_remote (has_hackathon_onsite, has_hackathon_remote) be sufficient. Note, you couldn’t represent two onsite one-day passes in that implementation. Thoughts? |
We need checkedin somewhere. I think it's fine to be on the MeetingRegistration record - we can make it clear in the model that it means "picked up a badge onsite at the meeting". I think we should keep "affiliation", but need to read the code to see if the datatracker is using it. Keeping it on MeetingRegistration makes sense. |
I like the concept. A couple key things to watch are, 1) that this is simplifying our lives, not just mooshing complexity/bugs around; and 2) that we're not replicating parts of the registration app that shouldn't leak into the datatracker.
I guess Name-wise, I like the ticket metaphor and would be inclined to call it a It might be too early for this level of minutiae, but I'd encourage avoiding
Checking an assumption, do we definitely need to support both onsite and remote registrations at the same meeting? I'd say we do either if we know we need it and/or if the reg system supports it, just so the datatracker side is expressive enough to capture everything. If not, life would be easier if the
As noted above, I think you're describing an attendance type and it should be labeled as such instead of the more vague "registration type". The business logic about what combinations make sense belongs on the reg side and datatracker should be able to represent whatever reg says and not second guess its records.
I don't know for sure about affiliation, but my instinct says it is a I think checkedin should be tied to the "ticket" that was checked in, not to the whole
This depends on what the datatracker is doing with the data. It'd be nice if they didn't move around, but might not be a hard requirement. I think we'll want to be able to refer at least to MeetingRegistrations so those should be stable. For things below that, we mostly need to be clear about which things we're allowed to point at with FKs and be careful about deletion policies. We don't want to get into a situation where the datatracker prevents reg from accurately recording the true registration data.
I prefer the better-normalized approach with the additional model to a raft of boolean flags. Not being able to represent multiple one-day passes makes it a non-starter. |
Robert posted his reply while I was writing my tome. His comment (and an out-of-band discussion) convinced me that the suggestion I have about putting "checkedin" on the "ticket" instances is really a different concept. |
This PR is replaced by #8408 |
Closing as it's been superseded |