/// <inheritdoc /> public RequestResult <UserComment> Add(UserComment userComment) { UserProfile profile = this.profileDelegate.GetUserProfile(userComment.UserProfileId).Payload; string? key = profile.EncryptionKey; if (key == null) { this.logger.LogError($"User does not have a key: ${userComment.UserProfileId}"); return(new RequestResult <UserComment>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Profile Key not set", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } Comment comment = userComment.ToDbModel(this.cryptoDelegate, key); DBResult <Comment> dbComment = this.commentDelegate.Add(comment); RequestResult <UserComment> result = new RequestResult <UserComment>() { ResourcePayload = UserComment.CreateFromDbModel(dbComment.Payload, this.cryptoDelegate, key), ResultStatus = dbComment.Status == DBStatusCode.Created ? ResultType.Success : ResultType.Error, ResultError = new RequestResultError() { ResultMessage = dbComment.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) }, }; return(result); }
/// <inheritdoc /> public RequestResult <IEnumerable <UserComment> > GetEntryComments(string hdId, string parentEntryId) { UserProfile profile = this.profileDelegate.GetUserProfile(hdId).Payload; string? key = profile.EncryptionKey; // Check that the key has been set if (key == null) { this.logger.LogError($"User does not have a key: ${hdId}"); return(new RequestResult <IEnumerable <UserComment> >() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Profile Key not set", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } DBResult <IEnumerable <Comment> > dbComments = this.commentDelegate.GetByParentEntry(hdId, parentEntryId); RequestResult <IEnumerable <UserComment> > result = new RequestResult <IEnumerable <UserComment> >() { ResourcePayload = UserComment.CreateListFromDbModel(dbComments.Payload, this.cryptoDelegate, key), TotalResultCount = dbComments.Payload.Count(), PageIndex = 0, PageSize = dbComments.Payload.Count(), ResultStatus = dbComments.Status == DBStatusCode.Read ? ResultType.Success : ResultType.Error, ResultError = dbComments.Status != DBStatusCode.Read ? new RequestResultError() { ResultMessage = dbComments.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) } : null, }; return(result); }
/// <summary> /// Formats the error from the given error type. /// </summary> /// <param name="message">The user friendly message.</param> /// <param name="actionType">Action type that caused the issue.</param> /// <returns>A RequestResultError encapsulating the action required.</returns> public static HealthGateway.Common.Models.RequestResultError ActionRequired(string message, ActionType actionType) { return(new Models.RequestResultError() { ResultMessage = message, ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState), ActionCode = actionType, }); }
public async void ShouldInsertUserProfileWithClosedRegistration() { Tuple <RequestResult <UserProfileModel>, UserProfileModel> result = await ExecuteInsertUserProfile(DBStatusCode.Error, RegistrationStatus.Closed, null); var actualResult = result.Item1; Assert.Equal(ResultType.Error, actualResult.ResultStatus); Assert.Equal(ErrorTranslator.InternalError(ErrorType.InvalidState), actualResult.ResultError.ErrorCode); Assert.Equal("Registration is closed", actualResult.ResultError.ResultMessage); }
/// <inheritdoc /> public PrimitiveRequestResult <bool> ValidateEmail(string hdid, Guid inviteKey) { this.logger.LogTrace($"Validating email... {inviteKey}"); PrimitiveRequestResult <bool> retVal = new (); MessagingVerification?emailVerification = this.messageVerificationDelegate.GetLastByInviteKey(inviteKey); if (emailVerification == null || emailVerification.HdId != hdid || emailVerification.Validated == true || emailVerification.Deleted == true) { // Invalid Verification Attempt emailVerification = this.messageVerificationDelegate.GetLastForUser(hdid, MessagingVerificationType.Email); if (emailVerification != null && !emailVerification.Validated) { emailVerification.VerificationAttempts++; this.messageVerificationDelegate.Update(emailVerification); } return(new PrimitiveRequestResult <bool>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Invalid Email Verification", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState), }, }); } else if (emailVerification.VerificationAttempts >= this.maxVerificationAttempts || emailVerification.ExpireDate < DateTime.UtcNow) { // Verification Expired retVal.ResultStatus = ResultType.ActionRequired; retVal.ResultError = ErrorTranslator.ActionRequired("Email Verification Expired", ActionType.Expired); } else { emailVerification.Validated = true; this.messageVerificationDelegate.Update(emailVerification); UserProfile userProfile = this.profileDelegate.GetUserProfile(hdid).Payload; userProfile.Email = emailVerification.Email !.To; // Gets the user email from the email sent. this.profileDelegate.Update(userProfile); // Update the notification settings this.notificationSettingsService.QueueNotificationSettings(new NotificationSettingsRequest(userProfile, userProfile.Email, userProfile.SMSNumber)); retVal.ResultStatus = ResultType.Success; } this.logger.LogDebug($"Finished validating email: {JsonConvert.SerializeObject(retVal)}"); return(retVal); }
/// <inheritdoc /> public RequestResult <IEnumerable <UserNote> > GetNotes(string hdId, int page = 0, int pageSize = 500) { int offset = page * pageSize; DBResult <IEnumerable <Note> > dbNotes = this.noteDelegate.GetNotes(hdId, offset, pageSize); UserProfile profile = this.profileDelegate.GetUserProfile(hdId).Payload; string? key = profile.EncryptionKey; // If there is no key yet, generate one and store it in the profile. Only valid while not all profiles have a encryption key. if (key == null) { this.logger.LogInformation($"First time note retrieval with key for user ${hdId}"); key = this.EncryptFirstTime(profile, dbNotes.Payload); dbNotes = this.noteDelegate.GetNotes(hdId, offset, pageSize); } // Check that the key has been set if (key == null) { this.logger.LogError($"User does not have a key: ${hdId}"); return(new RequestResult <IEnumerable <UserNote> >() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Profile Key not set", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } RequestResult <IEnumerable <UserNote> > result = new RequestResult <IEnumerable <UserNote> >() { ResourcePayload = UserNote.CreateListFromDbModel(dbNotes.Payload, this.cryptoDelegate, key), PageIndex = page, PageSize = pageSize, TotalResultCount = dbNotes.Payload.ToList().Count, ResultStatus = dbNotes.Status == DBStatusCode.Read ? ResultType.Success : ResultType.Error, ResultError = dbNotes.Status == DBStatusCode.Read ? null : new RequestResultError() { ResultMessage = dbNotes.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) }, }; return(result); }
/// <inheritdoc /> public async Task <RequestResult <UserProfileModel> > CreateUserProfile(CreateUserRequest createProfileRequest, Uri hostUri, string bearerToken) { this.logger.LogTrace($"Creating user profile... {JsonSerializer.Serialize(createProfileRequest)}"); string registrationStatus = this.configurationService.GetConfiguration().WebClient.RegistrationStatus; RequestResult <UserProfileModel> requestResult = new RequestResult <UserProfileModel>(); if (registrationStatus == RegistrationStatus.Closed) { requestResult.ResultStatus = ResultType.Error; requestResult.ResultError = new RequestResultError() { ResultMessage = "Registration is closed", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; this.logger.LogWarning($"Registration is closed. {JsonSerializer.Serialize(createProfileRequest)}"); return(requestResult); } string hdid = createProfileRequest.Profile.HdId; MessagingVerification?emailInvite = null; if (registrationStatus == RegistrationStatus.InviteOnly) { if (!Guid.TryParse(createProfileRequest.InviteCode, out Guid inviteKey)) { requestResult.ResultStatus = ResultType.Error; requestResult.ResultError = new RequestResultError() { ResultMessage = "Invalid email invite", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; this.logger.LogWarning($"Invalid email invite code. {JsonSerializer.Serialize(createProfileRequest)}"); return(requestResult); } emailInvite = this.emailInviteDelegate.GetByInviteKey(inviteKey); bool hdidIsValid = string.IsNullOrEmpty(emailInvite?.HdId) || (emailInvite?.HdId == createProfileRequest.Profile.HdId); // Fails if... // Email invite not found or // Email invite was already validated or // Email's recipient is not found // Email invite must have a blank/null HDID or be the same as the one in the request // Email address doesn't match the invite if (emailInvite == null || (emailInvite.Email == null || emailInvite.Email.To == null || emailInvite.Validated || !hdidIsValid || !emailInvite.Email.To.Equals(createProfileRequest.Profile.Email, StringComparison.OrdinalIgnoreCase))) { requestResult.ResultStatus = ResultType.Error; requestResult.ResultError = new RequestResultError() { ResultMessage = "Invalid email invite", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; this.logger.LogWarning($"Invalid email invite. {JsonSerializer.Serialize(createProfileRequest)}"); return(requestResult); } } PrimitiveRequestResult <bool> isMimimunAgeResult = await this.ValidateMinimumAge(hdid).ConfigureAwait(true); if (isMimimunAgeResult.ResultStatus != ResultType.Success) { requestResult.ResultStatus = isMimimunAgeResult.ResultStatus; requestResult.ResultError = isMimimunAgeResult.ResultError; return(requestResult); } else if (!isMimimunAgeResult.ResourcePayload) { requestResult.ResultStatus = ResultType.Error; requestResult.ResultError = new RequestResultError() { ResultMessage = "Patient under minimum age", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; this.logger.LogWarning($"Patient under minimum age. {JsonSerializer.Serialize(createProfileRequest)}"); return(requestResult); } string? requestedSMSNumber = createProfileRequest.Profile.SMSNumber; string? requestedEmail = createProfileRequest.Profile.Email; UserProfile newProfile = createProfileRequest.Profile; newProfile.Email = string.Empty; newProfile.SMSNumber = null; newProfile.CreatedBy = hdid; newProfile.UpdatedBy = hdid; newProfile.EncryptionKey = this.cryptoDelegate.GenerateKey(); DBResult <UserProfile> insertResult = this.userProfileDelegate.InsertUserProfile(newProfile); if (insertResult.Status == DBStatusCode.Created) { // Update the notification settings NotificationSettingsRequest notificationRequest = this.UpdateNotificationSettings(newProfile, requestedSMSNumber); if (emailInvite != null) { // Validates the invite email emailInvite.Validated = true; emailInvite.HdId = hdid; this.emailInviteDelegate.Update(emailInvite); } if (!string.IsNullOrWhiteSpace(requestedEmail)) { this.emailQueueService.QueueNewInviteEmail(hdid, requestedEmail, hostUri); } if (!string.IsNullOrWhiteSpace(requestedSMSNumber)) { this.logger.LogInformation($"Sending sms invite for user ${hdid}"); MessagingVerification messagingVerification = new MessagingVerification(); messagingVerification.HdId = hdid; messagingVerification.SMSNumber = requestedSMSNumber; messagingVerification.SMSValidationCode = notificationRequest.SMSVerificationCode; messagingVerification.VerificationType = MessagingVerificationType.SMS; messagingVerification.ExpireDate = DateTime.MaxValue; this.messageVerificationDelegate.Insert(messagingVerification); } requestResult.ResourcePayload = UserProfileModel.CreateFromDbModel(insertResult.Payload); requestResult.ResultStatus = ResultType.Success; } this.logger.LogDebug($"Finished creating user profile. {JsonSerializer.Serialize(insertResult)}"); return(requestResult); }
/// <inheritdoc /> public RequestResult <Communication> Update(Communication communication) { if (communication.EffectiveDateTime < communication.ExpiryDateTime) { this.logger.LogTrace($"Updating communication... {JsonSerializer.Serialize(communication)}"); DBResult <HealthGateway.Database.Models.Communication> dbResult = this.communicationDelegate.Update(communication.ToDbModel()); return(new RequestResult <Communication>() { ResourcePayload = new Communication(dbResult.Payload), ResultStatus = dbResult.Status == DBStatusCode.Updated ? ResultType.Success : ResultType.Error, ResultError = dbResult.Status == DBStatusCode.Updated ? null : new RequestResultError() { ResultMessage = dbResult.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) }, }); } else { return(new RequestResult <Communication>() { ResourcePayload = null, ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Effective Date should be before Expiry Date.", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } }
/// <inheritdoc /> public RequestResult <Communication> Add(Communication communication) { this.logger.LogTrace($"Communication recieved: {JsonSerializer.Serialize(communication)}"); if (communication.CommunicationTypeCode == CommunicationType.Email) { if (communication.Text.Length == 0 || communication.Subject.Length == 0) { throw new ArgumentException("One of: Email Subject, Email Content is invalid."); } communication.EffectiveDateTime = DateTime.UtcNow; communication.ExpiryDateTime = DateTime.UtcNow; } this.logger.LogTrace($"Adding communication... {JsonSerializer.Serialize(communication)}"); if (communication.EffectiveDateTime < communication.ExpiryDateTime) { this.logger.LogTrace($"Adding communication... {JsonSerializer.Serialize(communication)}"); DBResult <HealthGateway.Database.Models.Communication> dbResult = this.communicationDelegate.Add(communication.ToDbModel()); return(new RequestResult <Communication>() { ResourcePayload = new Communication(dbResult.Payload), ResultStatus = dbResult.Status == DBStatusCode.Created ? ResultType.Success : ResultType.Error, ResultError = dbResult.Status == DBStatusCode.Created ? null : new RequestResultError() { ResultMessage = dbResult.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) }, }); } else { return(new RequestResult <Communication>() { ResourcePayload = null, ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Effective Date should be before Expiry Date.", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } }
/// <inheritdoc /> public RequestResult <Communication> Delete(Communication communication) { if (communication.CommunicationStatusCode == CommunicationStatus.Processed) { this.logger.LogError($"Processed communication can't be deleted."); return(new RequestResult <Communication>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Processed communication can't be deleted.", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }); } DBResult <HealthGateway.Database.Models.Communication> dbResult = this.communicationDelegate.Delete(communication.ToDbModel()); RequestResult <Communication> result = new RequestResult <Communication>() { ResourcePayload = new Communication(dbResult.Payload), ResultStatus = dbResult.Status == DBStatusCode.Deleted ? ResultType.Success : ResultType.Error, ResultError = dbResult.Status == DBStatusCode.Deleted ? null : new RequestResultError() { ResultMessage = dbResult.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database) }, }; return(result); }
private void ReceiveEvent(object sender, NpgsqlNotificationEventArgs e) { this.logger.LogInformation($"Event received on channel {Channel}"); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IgnoreNullValues = true, WriteIndented = true, }; options.Converters.Add(new JsonStringEnumConverter()); BannerChangeEvent?changeEvent = JsonSerializer.Deserialize <BannerChangeEvent>(e.Payload, options); if (this.CommunicationService != null && changeEvent != null && changeEvent.Data != null) { DateTime utcnow = DateTime.UtcNow; RequestResult <Communication> cacheEntry = new RequestResult <Communication>(); if (changeEvent.Action == InsertAction || changeEvent.Action == UpdateAction) { Communication comm = changeEvent.Data; if (utcnow >= comm.EffectiveDateTime && utcnow <= comm.ExpiryDateTime) { cacheEntry.ResultStatus = Common.Constants.ResultType.Success; cacheEntry.ResourcePayload = comm; this.logger.LogInformation("Active Banner inserted or updated in DB"); this.CommunicationService.SetActiveBannerCache(cacheEntry); } } else if (changeEvent.Action == DeleteAction) { RequestResult <Communication> currentBanner = this.CommunicationService.GetActiveBanner(); if (currentBanner.ResourcePayload != null && currentBanner.ResourcePayload.Id == changeEvent.Data.Id) { cacheEntry.ResultStatus = Common.Constants.ResultType.Error; cacheEntry.ResultError = new RequestResultError() { ResultMessage = "Active Banner deleted from DB", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; this.logger.LogInformation("Active Banner deleted from DB"); this.CommunicationService.SetActiveBannerCache(cacheEntry); } } } }
/// <inheritdoc/> public async System.Threading.Tasks.Task <RequestResult <PatientModel> > GetPatient(string identifier, PatientIdentifierType identifierType = PatientIdentifierType.HDID) { using Activity? activity = Source.StartActivity("GetPatient"); RequestResult <PatientModel> requestResult = new RequestResult <PatientModel>(); PatientModel?patient = this.GetFromCache(identifier, identifierType); if (patient == null) { switch (identifierType) { case PatientIdentifierType.HDID: this.logger.LogDebug("Performing Patient lookup by HDID"); requestResult = await this.patientDelegate.GetDemographicsByHDIDAsync(identifier).ConfigureAwait(true); break; case PatientIdentifierType.PHN: this.logger.LogDebug("Performing Patient lookup by PHN"); requestResult = await this.patientDelegate.GetDemographicsByPHNAsync(identifier).ConfigureAwait(true); break; default: this.logger.LogDebug($"Failed Patient lookup unknown PatientIdentifierType"); requestResult.ResultStatus = ResultType.Error; requestResult.ResultError = new RequestResultError() { ResultMessage = $"Internal Error: PatientIdentifierType is unknown '{identifierType.ToString()}'", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }; break; } if (requestResult.ResultStatus == ResultType.Success && requestResult.ResourcePayload != null) { this.CachePatient(requestResult.ResourcePayload); } } else { this.logger.LogDebug("Patient fetched from Cache"); requestResult.ResourcePayload = patient; requestResult.ResultStatus = ResultType.Success; } activity?.Stop(); return(requestResult); }
public void ShouldDeleteProcessedCommunicationReturnError() { // Sample communication to test Communication comm = new Communication() { Id = Guid.NewGuid(), CommunicationStatusCode = CommunicationStatus.Processed, Text = "Test update communication", Subject = "Testing update communication", EffectiveDateTime = new DateTime(2020, 07, 04), ExpiryDateTime = new DateTime(2020, 07, 07), }; RequestResult <Communication> expectedResult = new RequestResult <Communication>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Processed communication can't be deleted.", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState) }, }; RequestResult <Communication> actualResult = DeleteCommunication(comm, DBStatusCode.Error); // Check result Assert.True(expectedResult.IsDeepEqual(actualResult)); }
/// <inheritdoc /> public async Task <RequestResult <UserProfileModel> > CreateUserProfile(CreateUserRequest createProfileRequest, DateTime jwtAuthTime) { this.logger.LogTrace($"Creating user profile... {JsonSerializer.Serialize(createProfileRequest)}"); string registrationStatus = this.configurationService.GetConfiguration().WebClient.RegistrationStatus; if (registrationStatus == RegistrationStatus.Closed) { this.logger.LogWarning($"Registration is closed. {JsonSerializer.Serialize(createProfileRequest)}"); return(new RequestResult <UserProfileModel>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Registration is closed", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState), }, }); } // Validate registration age string hdid = createProfileRequest.Profile.HdId; PrimitiveRequestResult <bool> isMimimunAgeResult = await this.ValidateMinimumAge(hdid).ConfigureAwait(true); if (isMimimunAgeResult.ResultStatus != ResultType.Success) { return(new RequestResult <UserProfileModel>() { ResultStatus = isMimimunAgeResult.ResultStatus, ResultError = isMimimunAgeResult.ResultError, }); } else if (!isMimimunAgeResult.ResourcePayload) { this.logger.LogWarning($"Patient under minimum age. {JsonSerializer.Serialize(createProfileRequest)}"); return(new RequestResult <UserProfileModel>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = "Patient under minimum age", ErrorCode = ErrorTranslator.InternalError(ErrorType.InvalidState), }, }); } // Create profile UserProfile newProfile = new () { HdId = hdid, IdentityManagementId = createProfileRequest.Profile.IdentityManagementId, AcceptedTermsOfService = createProfileRequest.Profile.AcceptedTermsOfService, Email = string.Empty, SMSNumber = null, CreatedBy = hdid, UpdatedBy = hdid, LastLoginDateTime = jwtAuthTime, EncryptionKey = this.cryptoDelegate.GenerateKey(), }; DBResult <UserProfile> insertResult = this.userProfileDelegate.InsertUserProfile(newProfile); if (insertResult.Status == DBStatusCode.Created) { UserProfile createdProfile = insertResult.Payload; string? requestedSMSNumber = createProfileRequest.Profile.SMSNumber; string? requestedEmail = createProfileRequest.Profile.Email; // Add email verification if (!string.IsNullOrWhiteSpace(requestedEmail)) { this.userEmailService.CreateUserEmail(hdid, requestedEmail); } // Add SMS verification if (!string.IsNullOrWhiteSpace(requestedSMSNumber)) { this.userSMSService.CreateUserSMS(hdid, requestedSMSNumber); } this.notificationSettingsService.QueueNotificationSettings(new NotificationSettingsRequest(createdProfile, requestedEmail, requestedSMSNumber)); this.logger.LogDebug($"Finished creating user profile. {JsonSerializer.Serialize(insertResult)}"); return(new RequestResult <UserProfileModel>() { ResourcePayload = UserProfileModel.CreateFromDbModel(insertResult.Payload), ResultStatus = ResultType.Success, }); } else { this.logger.LogError($"Error creating user profile. {JsonSerializer.Serialize(insertResult)}"); return(new RequestResult <UserProfileModel>() { ResultStatus = ResultType.Error, ResultError = new RequestResultError() { ResultMessage = insertResult.Message, ErrorCode = ErrorTranslator.ServiceError(ErrorType.CommunicationInternal, ServiceType.Database), }, }); } }