better error handling for User.

Adding an endpoint that allows user to create background job errors.
This commit is contained in:
Jason Kulatunga 2024-01-21 09:58:56 -08:00
parent efd2fb3655
commit ac5359c332
No known key found for this signature in database
4 changed files with 53 additions and 4 deletions

View File

@ -13,7 +13,8 @@ type BackgroundJob struct {
User User `json:"user,omitempty"` //SECURITY: user and user.id will be set by the repository service
UserID uuid.UUID `json:"user_id"`
JobType pkg.BackgroundJobType `json:"job_type"`
JobType pkg.BackgroundJobType `json:"job_type"`
//this should be JSON encoded data from BackgroundJobSyncData or
Data datatypes.JSON `gorm:"column:data;type:text;serializer:json" json:"data,omitempty"`
JobStatus pkg.BackgroundJobStatus `json:"job_status"`
LockedTime *time.Time `json:"locked_time"`

View File

@ -239,6 +239,46 @@ func ListBackgroundJobs(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "data": backgroundJobs})
}
// CreateBackgroundJobError this function is used to store error data related to a Source/Provider connection operation that fails in the client-side
// - client errors occur when the OAuth provider sends back an error message (error, error_description query string parameters) or when the code -> access token swap results in an error.
// - server side errors occur for a number of reasons (unable to initialize client, unable to store crednetial in db, unable to sync 1 or more FHIR resources from a patient's medical record)
func CreateBackgroundJobError(c *gin.Context) {
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
var payload models.BackgroundJobSyncData
if err := c.ShouldBindJSON(&payload); err != nil {
logger.Errorln("An error occurred while parsing error data", err)
c.JSON(http.StatusBadRequest, gin.H{"success": false})
return
}
//override the job type to be an error
errJsonData, err := json.MarshalIndent(payload, "", " ")
if err != nil {
logger.Errorln("An error occurred re-encoding error data", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
now := time.Now()
backgroundJob := models.BackgroundJob{
JobType: pkg.BackgroundJobTypeSync,
JobStatus: pkg.BackgroundJobStatusFailed,
DoneTime: &now,
LockedTime: &now,
Data: errJsonData,
}
err = databaseRepo.CreateBackgroundJob(c, &backgroundJob)
if err != nil {
logger.Errorln("An error occurred while creating background job to store client-side error data", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
c.JSON(http.StatusOK, gin.H{"success": true})
}
// Utilities
func GetBackgroundContext(ginContext *gin.Context) context.Context {

View File

@ -89,9 +89,12 @@ func CreateReconnectSource(c *gin.Context) {
summary, err := BackgroundJobSyncResources(GetBackgroundContext(c), logger, databaseRepo, &sourceCred)
if err != nil {
err := fmt.Errorf("an error occurred while starting initial sync: %w", err)
logger.Errorln(err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
//errors from the background job will be wrapped and stored in the database, lets just return a generic error
// this is also important because these errors:
// 1. are not user facing - longer/scarier for users, and may show information that they are not equipped to resolve themselves.
// 2. lots of duplicate text ("an error occurred while...") due to wrapping as the error bubbles up the codebase.
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "initial record sync failed. See background jobs page for more details"})
return
}
@ -118,7 +121,11 @@ func SourceSync(c *gin.Context) {
if err != nil {
err := fmt.Errorf("an error occurred while syncing resources: %w", err)
logger.Errorln(err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
//errors from the background job will be wrapped and stored in the database, lets just return a generic error
// this is also important because these errors:
// 1. are not user facing - longer/scarier for users, and may show information that they are not equipped to resolve themselves.
// 2. lots of duplicate text ("an error occurred while...") due to wrapping as the error bubbles up the codebase.
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "record sync failed. See background jobs page for more details"})
return
}

View File

@ -85,6 +85,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) {
//secure.GET("/dashboard/:dashboardId", handler.GetDashboard)
secure.GET("/jobs", handler.ListBackgroundJobs)
secure.POST("/jobs/error", handler.CreateBackgroundJobError)
secure.POST("/query", handler.QueryResourceFhir)