diff --git a/patients/consumer.go b/patients/consumer.go index 356da6d..4389e19 100644 --- a/patients/consumer.go +++ b/patients/consumer.go @@ -147,9 +147,9 @@ func (p *PatientCDCConsumer) handleCDCEvent(event PatientCDCEvent) error { return err } - // Only send invite email if patient does not have a pending dexcom connect request, which also + // Only send invite email if patient does not have a pending connection request, which also // sends an email that provides a pathway towards claiming the account - if !event.PatientHasPendingDexcomConnection() { + if !event.PatientHasPendingConnection() { if err := p.applyInviteUpdate(event); err != nil { return err } @@ -177,16 +177,17 @@ func (p *PatientCDCConsumer) handleCDCEvent(event PatientCDCEvent) error { return p.sendUploadReminder(*event.FullDocument.UserId) } - if event.IsRequestDexcomConnectEvent() { - p.logger.Infow("processing dexcom connect email", "event", event) + connectionRequests := event.UpdateDescription.UpdatedFields.GetUpdatedConnectionRequests() + if len(connectionRequests) > 0 { + p.logger.Infow("processing connection requests", "event", event) if event.FullDocument.IsCustodial() { invite := confirmations.UpsertAccountSignupConfirmationJSONRequestBody{ - ClinicId: (*confirmations.ClinicId)(&event.FullDocument.ClinicId.Value), - InvitedBy: (*confirmations.TidepoolUserId)(event.FullDocument.InvitedBy), + ClinicId: &event.FullDocument.ClinicId.Value, + InvitedBy: event.FullDocument.InvitedBy, } - response, err := p.confirmations.UpsertAccountSignupConfirmationWithResponse(ctx, confirmations.UserId(*event.FullDocument.UserId), invite) + response, err := p.confirmations.UpsertAccountSignupConfirmationWithResponse(ctx, *event.FullDocument.UserId, invite) if err != nil { return fmt.Errorf("unable to upsert confirmation: %v", err) } @@ -198,26 +199,37 @@ func (p *PatientCDCConsumer) handleCDCEvent(event PatientCDCEvent) error { } } - templateName := "request_dexcom_connect" - - if event.FullDocument.IsCustodial() { - templateName = "request_dexcom_connect_custodial" + providers := map[string]struct{}{} + for _, r := range connectionRequests { + providers[r.ProviderName] = struct{}{} } - if event.FullDocument.DataSources != nil { - for _, source := range *event.FullDocument.DataSources { - if *source.ProviderName == DexcomDataSourceProviderName && *source.State == string(clinics.DataSourceStatePendingReconnect) { - templateName = "request_dexcom_reconnect" + errs := make([]error, 0, len(providers)) + for providerName, _ := range providers { + templatePrefix := fmt.Sprintf("request_%s_", providerName) + action := "connect" + if event.FullDocument.IsCustodial() { + action = "connect_custodial" + } + if event.FullDocument.DataSources != nil { + for _, source := range *event.FullDocument.DataSources { + if *source.ProviderName == providerName && *source.State == string(clinics.DataSourceStatePendingReconnect) { + action = "reconnect" + } } } - } - return p.sendDexcomConnectEmail( - *event.FullDocument.UserId, - event.FullDocument.ClinicId.Value, - *event.FullDocument.FullName, - templateName, - ) + templateName := templatePrefix + action + errs = append(errs, p.sendDexcomConnectEmail( + *event.FullDocument.UserId, + event.FullDocument.ClinicId.Value, + *event.FullDocument.FullName, + templateName, + )) + } + if err := errors.Join(errs...); err != nil { + return err + } } return nil diff --git a/patients/models.go b/patients/models.go index 95196be..03777d4 100644 --- a/patients/models.go +++ b/patients/models.go @@ -30,16 +30,6 @@ func (p PatientCDCEvent) IsUploadReminderEvent() bool { return lastUploadReminderTime != nil && lastUploadReminderTime.Value > 0 } -func (p PatientCDCEvent) IsRequestDexcomConnectEvent() bool { - if p.OperationType != cdc.OperationTypeUpdate && p.OperationType != cdc.OperationTypeReplace { - return false - } - if p.FullDocument.UserId == nil || p.UpdateDescription.UpdatedFields.LastRequestedDexcomConnectTime == nil { - return false - } - return p.UpdateDescription.UpdatedFields.LastRequestedDexcomConnectTime.Value > 0 -} - func (p PatientCDCEvent) IsProfileUpdateEvent() bool { if p.OperationType != cdc.OperationTypeInsert && p.OperationType != cdc.OperationTypeUpdate && p.OperationType != cdc.OperationTypeReplace { return false @@ -55,10 +45,10 @@ func (p PatientCDCEvent) IsPatientCreateFromExistingUserEvent() bool { return p.OperationType == cdc.OperationTypeInsert && !p.FullDocument.IsCustodial() } -func (p PatientCDCEvent) PatientHasPendingDexcomConnection() bool { +func (p PatientCDCEvent) PatientHasPendingConnection() bool { if p.FullDocument.DataSources != nil { for _, dataSource := range *p.FullDocument.DataSources { - if *dataSource.ProviderName == DexcomDataSourceProviderName && *dataSource.State == string(clinics.DataSourceStatePending) { + if *dataSource.State == string(clinics.DataSourceStatePending) { return true } } @@ -110,21 +100,31 @@ type CDCSummary struct { } type Patient struct { - Id *cdc.ObjectId `json:"_id" bson:"_id"` - ClinicId *cdc.ObjectId `json:"clinicId" bson:"clinicId"` - UserId *string `json:"userId" bson:"userId"` - BirthDate *string `json:"birthDate" bson:"birthDate"` - Email *string `json:"email" bson:"email"` - FullName *string `json:"fullName" bson:"fullName"` - Mrn *string `json:"mrn" bson:"mrn"` - TargetDevices *[]string `json:"targetDevices" bson:"targetDevices"` - DataSources *[]PatientDataSource `json:"dataSources" bson:"dataSources"` - Permissions *Permissions `json:"permissions" bson:"permissions"` - IsMigrated bool `json:"isMigrated" bson:"isMigrated"` - InvitedBy *string `json:"invitedBy" bson:"invitedBy"` - LastRequestedDexcomConnectTime *cdc.Date `json:"lastRequestedDexcomConnectTime" bson:"lastRequestedDexcomConnectTime"` - LastUploadReminderTime *cdc.Date `json:"lastUploadReminderTime" bson:"lastUploadReminderTime"` - Summary *CDCSummary `json:"summary" bson:"summary"` + Id *cdc.ObjectId `json:"_id" bson:"_id"` + ClinicId *cdc.ObjectId `json:"clinicId" bson:"clinicId"` + UserId *string `json:"userId" bson:"userId"` + BirthDate *string `json:"birthDate" bson:"birthDate"` + Email *string `json:"email" bson:"email"` + FullName *string `json:"fullName" bson:"fullName"` + Mrn *string `json:"mrn" bson:"mrn"` + TargetDevices *[]string `json:"targetDevices" bson:"targetDevices"` + DataSources *[]PatientDataSource `json:"dataSources" bson:"dataSources"` + Permissions *Permissions `json:"permissions" bson:"permissions"` + IsMigrated bool `json:"isMigrated" bson:"isMigrated"` + InvitedBy *string `json:"invitedBy" bson:"invitedBy"` + LastRequestedDexcomConnectTime *cdc.Date `json:"lastRequestedDexcomConnectTime" bson:"lastRequestedDexcomConnectTime"` + LastUploadReminderTime *cdc.Date `json:"lastUploadReminderTime" bson:"lastUploadReminderTime"` + Summary *CDCSummary `json:"summary" bson:"summary"` + ProviderConnectionRequests ProviderConnectionRequests `json:"providerConnectionRequests" bson:"providerConnectionRequests"` +} + +type ProviderConnectionRequests map[string]ConnectionRequests + +type ConnectionRequests []ConnectionRequest + +type ConnectionRequest struct { + ProviderName string `json:"providerName" bson:"providerName"` + CreatedTime cdc.Date `json:"createdTime" bson:"createdTime"` } func (p PatientCDCEvent) CreateDataSourceBody(source clients.DataSource) clinics.DataSource { @@ -170,4 +170,27 @@ func (u UpdateDescription) applyUpdatesToExistingProfile(profile map[string]inte type UpdatedFields struct { Patient + + // Partial updates to nested fields are encoded using dot notation in CDC events + ProviderConnectionRequestsDexcom ConnectionRequests `bson:"providerConnectionRequests.dexcom"` + ProviderConnectionRequestsTwiist ConnectionRequests `bson:"providerConnectionRequests.twiist"` +} + +func (u UpdatedFields) GetUpdatedConnectionRequests() ConnectionRequests { + var requests ConnectionRequests + if u.ProviderConnectionRequests != nil { + for _, r := range u.ProviderConnectionRequests { + for _, v := range r { + requests = append(requests, v) + } + } + } + for _, v := range u.ProviderConnectionRequestsDexcom { + requests = append(requests, v) + } + for _, v := range u.ProviderConnectionRequestsTwiist { + requests = append(requests, v) + } + return requests } +