github.com/companieshouse/insolvency-api@v0.0.0-20231024103413-440c973d9e9b/handlers/practitioner_resource.go (about) 1 package handlers 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/companieshouse/chs.go/log" 9 "github.com/companieshouse/insolvency-api/constants" 10 "github.com/companieshouse/insolvency-api/dao" 11 "github.com/companieshouse/insolvency-api/models" 12 "github.com/companieshouse/insolvency-api/service" 13 "github.com/companieshouse/insolvency-api/transformers" 14 "github.com/companieshouse/insolvency-api/utils" 15 "github.com/gorilla/mux" 16 ) 17 18 // HandleCreatePractitionersResource updates the insolvency resource with the 19 // incoming list of practitioners 20 func HandleCreatePractitionersResource(svc dao.Service, helperService utils.HelperService) http.Handler { 21 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 22 23 // Check transaction is valid 24 transactionID, isValidTransaction := utils.ValidateTransaction(helperService, req, w, "practitioners", service.CheckIfTransactionClosed) 25 if !isValidTransaction { 26 return 27 } 28 29 // Decode the incoming request to create a list of practitioners 30 var request models.PractitionerRequest 31 err := json.NewDecoder(req.Body).Decode(&request) 32 isValidDecoded := helperService.HandleBodyDecodedValidation(w, req, transactionID, err) 33 if !isValidDecoded { 34 return 35 } 36 37 // Validate all mandatory fields 38 errs := utils.Validate(request) 39 isValidMarshallToDB := helperService.HandleMandatoryFieldValidation(w, req, errs) 40 if !isValidMarshallToDB { 41 return 42 } 43 44 // Validates that the provided practitioner details are in the correct format 45 validationErrs, err := service.ValidatePractitionerDetails(svc, transactionID, request) 46 if err != nil { 47 log.ErrorR(req, err) 48 m := models.NewMessageResponse("failed to validate the practitioner request supplied") 49 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 50 return 51 } 52 if validationErrs != "" { 53 log.ErrorR(req, fmt.Errorf("invalid request - failed validation on the following: %s", validationErrs)) 54 m := models.NewMessageResponse("invalid request body: " + validationErrs) 55 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 56 return 57 } 58 59 // Check if practitioner role supplied is valid 60 if ok := constants.IsInRoleList(request.Role); !ok { 61 log.ErrorR(req, fmt.Errorf("invalid practitioner role")) 62 m := models.NewMessageResponse(fmt.Sprintf("the practitioner role supplied is not valid %s", request.Role)) 63 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 64 return 65 } 66 67 practitionerDao := transformers.PractitionerResourceRequestToDB(&request, transactionID) 68 69 // Store practitioners resource in Mongo 70 err, statusCode := svc.CreatePractitionersResource(practitionerDao, transactionID) 71 if err != nil { 72 log.ErrorR(req, err) 73 m := models.NewMessageResponse(err.Error()) 74 utils.WriteJSONWithStatus(w, req, m, statusCode) 75 return 76 } 77 78 log.InfoR(req, fmt.Sprintf("successfully added practitioners resource with transaction ID: %s, to mongo", transactionID)) 79 80 utils.WriteJSONWithStatus(w, req, transformers.PractitionerResourceDaoToCreatedResponse(practitionerDao), http.StatusCreated) 81 }) 82 } 83 84 // HandleGetPractitionerResources retrieves a list of practitioners for the insolvency case with 85 // the specified transactionID 86 func HandleGetPractitionerResources(svc dao.Service) http.Handler { 87 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 88 // Check for a transaction id in request 89 vars := mux.Vars(req) 90 transactionID := utils.GetTransactionIDFromVars(vars) 91 if transactionID == "" { 92 log.ErrorR(req, fmt.Errorf("there is no transaction id in the url path")) 93 m := models.NewMessageResponse("transaction id is not in the url path") 94 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 95 return 96 } 97 98 log.InfoR(req, fmt.Sprintf("start GET request for practitioners resource with transaction id: %s", transactionID)) 99 100 practitionerResources, err := svc.GetPractitionerResources(transactionID) 101 if err != nil { 102 log.ErrorR(req, err) 103 m := models.NewMessageResponse(err.Error()) 104 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 105 return 106 } 107 if practitionerResources == nil { 108 log.ErrorR(req, fmt.Errorf("insolvency case for transaction %s not found", transactionID)) 109 m := models.NewMessageResponse("there was a problem handling your request for insolvency case with transaction ID: " + transactionID + " not found") 110 utils.WriteJSONWithStatus(w, req, m, http.StatusNotFound) 111 return 112 } 113 if len(practitionerResources) == 0 { 114 log.ErrorR(req, fmt.Errorf("practitioners for insolvency case with transaction %s not found", transactionID)) 115 m := models.NewMessageResponse("there was a problem handling your request for insolvency case with transaction: " + transactionID + " there are no practitioners assigned to this case") 116 utils.WriteJSONWithStatus(w, req, m, http.StatusNotFound) 117 return 118 } 119 120 utils.WriteJSONWithStatus(w, req, transformers.PractitionerResourceDaoListToCreatedResponseList(practitionerResources), http.StatusOK) 121 }) 122 } 123 124 // HandleGetPractitionerResource retrieves a practitioner with the specified practitionerID 125 // on the insolvency case with the specified transactionID 126 func HandleGetPractitionerResource(svc dao.Service) http.Handler { 127 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 128 // Check for a transaction id in request 129 vars := mux.Vars(req) 130 transactionID, practitionerID, err := getTransactionIDAndPractitionerIDFromVars(vars) 131 if err != nil { 132 log.ErrorR(req, err) 133 m := models.NewMessageResponse(err.Error()) 134 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 135 return 136 } 137 138 log.InfoR(req, fmt.Sprintf("start GET request for practitioner resource with transaction id: %s and practitioner id: %s", transactionID, practitionerID)) 139 140 // Get practitioner from DB 141 practitioner, err := svc.GetPractitionerResource(practitionerID, transactionID) 142 if err != nil { 143 log.ErrorR(req, fmt.Errorf("failed to get practitioner with id [%s]: [%s]", practitionerID, err)) 144 m := models.NewMessageResponse("there was a problem handling your request") 145 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 146 return 147 } 148 149 // Check if practitioner returned is empty 150 if practitioner == (models.PractitionerResourceDao{}) { 151 message := fmt.Sprintf("practitioner with ID [%s] not found", practitionerID) 152 log.Debug(message) 153 m := models.NewMessageResponse(message) 154 utils.WriteJSONWithStatus(w, req, m, http.StatusNotFound) 155 return 156 } 157 158 // Successfully retrieved practitioner 159 utils.WriteJSONWithStatus(w, req, transformers.PractitionerResourceDaoToCreatedResponse(&practitioner), http.StatusOK) 160 }) 161 } 162 163 // HandleDeletePractitioner deletes a practitioner from the insolvency case with 164 // the specified transactionID and IPCode 165 func HandleDeletePractitioner(svc dao.Service) http.Handler { 166 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 167 // Check for a transaction id in request 168 vars := mux.Vars(req) 169 transactionID, practitionerID, err := getTransactionIDAndPractitionerIDFromVars(vars) 170 if err != nil { 171 log.ErrorR(req, err) 172 m := models.NewMessageResponse(err.Error()) 173 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 174 return 175 } 176 177 log.InfoR(req, fmt.Sprintf("start DELETE request for practitioner resource with transaction id: %s and practitioner id: %s", transactionID, practitionerID)) 178 179 // Check if transaction is closed 180 isTransactionClosed, err, httpStatus := service.CheckIfTransactionClosed(transactionID, req) 181 if err != nil { 182 log.ErrorR(req, fmt.Errorf(constants.MsgErrorCheckTransactionStatus, transactionID, err)) 183 m := models.NewMessageResponse(fmt.Sprintf(constants.MsgErrorCheckTransactionStatus, transactionID, err)) 184 utils.WriteJSONWithStatus(w, req, m, httpStatus) 185 return 186 } 187 if isTransactionClosed { 188 log.ErrorR(req, fmt.Errorf(constants.MsgNoUpdateTransactionClosed, transactionID)) 189 m := models.NewMessageResponse(fmt.Sprintf(constants.MsgNoUpdateTransactionClosed, transactionID)) 190 utils.WriteJSONWithStatus(w, req, m, httpStatus) 191 return 192 } 193 194 // Delete practitioner from Mongo 195 err, statusCode := svc.DeletePractitioner(practitionerID, transactionID) 196 if err != nil { 197 log.ErrorR(req, err) 198 m := models.NewMessageResponse(err.Error()) 199 utils.WriteJSONWithStatus(w, req, m, statusCode) 200 return 201 } 202 203 log.InfoR(req, fmt.Sprintf("successfully deleted practitioner with transaction ID: %s and practitioner ID: %s, from mongo", transactionID, practitionerID)) 204 205 w.Header().Set("Content-Type", "application/json") 206 w.WriteHeader(statusCode) 207 }) 208 } 209 210 // HandleAppointPractitioner adds appointment details to a practitioner resource on a transaction 211 func HandleAppointPractitioner(svc dao.Service, helperService utils.HelperService) http.Handler { 212 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 213 214 // Check transaction id & practitioner id exist in path 215 transactionID, practitionerID, err := getTransactionIDAndPractitionerIDFromVars(mux.Vars(req)) 216 if err != nil { 217 log.ErrorR(req, err) 218 m := models.NewMessageResponse(err.Error()) 219 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 220 return 221 } 222 223 log.InfoR(req, fmt.Sprintf("start POST request for practitioner appointment with transaction ID: [%s] and practitioner ID: [%s]", transactionID, practitionerID)) 224 225 // Check if transaction is closed 226 isTransactionClosed, err, httpStatus := service.CheckIfTransactionClosed(transactionID, req) 227 isValidTransactionNotClosed := helperService.HandleTransactionNotClosedValidation(w, req, transactionID, isTransactionClosed, httpStatus, err) 228 if !isValidTransactionNotClosed { 229 return 230 } 231 232 // Decode the incoming request to create a list of practitioners 233 var request models.PractitionerAppointment 234 err = json.NewDecoder(req.Body).Decode(&request) 235 isValidDecoded := helperService.HandleBodyDecodedValidation(w, req, transactionID, err) 236 if !isValidDecoded { 237 return 238 } 239 240 // Validate all mandatory fields 241 errs := utils.Validate(request) 242 isValidMarshallToDB := helperService.HandleMandatoryFieldValidation(w, req, errs) 243 if !isValidMarshallToDB { 244 return 245 } 246 247 // Check if made_by supplied is valid 248 if ok := constants.IsAppointmentMadeByInList(request.MadeBy); !ok { 249 log.ErrorR(req, fmt.Errorf("invalid appointment made_by")) 250 m := models.NewMessageResponse(fmt.Sprintf("the appointment made_by supplied is not valid: [%s]", request.MadeBy)) 251 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 252 return 253 } 254 255 // Validate all appointment details are of the correct format and criteria 256 validationErrs, err := service.ValidateAppointmentDetails(svc, request, transactionID, practitionerID, req) 257 if err != nil { 258 log.ErrorR(req, fmt.Errorf("failed to validate appointment details: [%s]", err)) 259 m := models.NewMessageResponse(fmt.Sprintf("there was a problem handling your request for transaction ID [%s]", transactionID)) 260 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 261 return 262 } 263 if validationErrs != "" { 264 log.ErrorR(req, fmt.Errorf("invalid request - failed validation on the following: %s", validationErrs)) 265 m := models.NewMessageResponse("invalid request body: " + validationErrs) 266 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 267 return 268 } 269 270 practitionerAppointmentDao := transformers.PractitionerAppointmentRequestToDB(&request, transactionID, practitionerID) 271 272 // Store appointment in DB 273 err, statusCode := svc.AppointPractitioner(practitionerAppointmentDao, transactionID, practitionerID) 274 if err != nil { 275 log.ErrorR(req, err) 276 m := models.NewMessageResponse(err.Error()) 277 utils.WriteJSONWithStatus(w, req, m, statusCode) 278 return 279 } 280 281 log.InfoR(req, fmt.Sprintf("successfully added practitioner appointment with transaction ID [%s] and practitioner ID [%s] to mongo", transactionID, practitionerID)) 282 283 practitioner, err := svc.GetPractitionerResource(practitionerID, transactionID) 284 if err != nil { 285 log.ErrorR(req, err) 286 m := models.NewMessageResponse(err.Error()) 287 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 288 return 289 } 290 291 // Check if practitioner is empty (not found). This should not happen, as appointment has just been added. 292 if practitioner == (models.PractitionerResourceDao{}) { 293 msg := fmt.Sprintf("practitionerID [%s] not found for transactionID [%s]", practitionerID, transactionID) 294 log.InfoR(req, msg) 295 m := models.NewMessageResponse(msg) 296 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 297 return 298 } 299 300 appointmentResponse := transformers.PractitionerAppointmentDaoToResponse(*practitioner.Appointment) 301 302 utils.WriteJSONWithStatus(w, req, appointmentResponse, http.StatusCreated) 303 }) 304 } 305 306 // HandleGetPractitionerAppointment retrieves appointment details 307 // for the specified transactionID and practitionerID 308 func HandleGetPractitionerAppointment(svc dao.Service) http.Handler { 309 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 310 311 vars := mux.Vars(req) 312 transactionID, practitionerID, err := getTransactionIDAndPractitionerIDFromVars(vars) 313 if err != nil { 314 log.ErrorR(req, err) 315 m := models.NewMessageResponse(err.Error()) 316 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 317 return 318 } 319 320 log.InfoR(req, fmt.Sprintf("start GET request for appointments resource with transaction ID: [%s] and practitioner ID: [%s]", transactionID, practitionerID)) 321 322 practitioner, err := svc.GetPractitionerResource(practitionerID, transactionID) 323 if err != nil { 324 log.ErrorR(req, err) 325 m := models.NewMessageResponse(err.Error()) 326 utils.WriteJSONWithStatus(w, req, m, http.StatusInternalServerError) 327 return 328 } 329 330 // Check if practitioner is empty (not found). 331 if practitioner == (models.PractitionerResourceDao{}) { 332 msg := fmt.Sprintf("practitionerID [%s] not found for transactionID [%s]", practitionerID, transactionID) 333 log.InfoR(req, msg) 334 m := models.NewMessageResponse(msg) 335 utils.WriteJSONWithStatus(w, req, m, http.StatusNotFound) 336 return 337 } 338 339 // Check if practitioner has an appointment 340 if practitioner.Appointment == nil { 341 msg := fmt.Sprintf("No appointment found for practitionerID [%s] and transactionID [%s]", practitionerID, transactionID) 342 log.InfoR(req, msg) 343 m := models.NewMessageResponse(msg) 344 utils.WriteJSONWithStatus(w, req, m, http.StatusNotFound) 345 return 346 } 347 348 appointmentResponse := transformers.PractitionerAppointmentDaoToResponse(*practitioner.Appointment) 349 350 utils.WriteJSONWithStatus(w, req, appointmentResponse, http.StatusOK) 351 }) 352 } 353 354 // HandleDeletePractitionerAppointment deletes an appointment 355 // for the specified transactionID and practitionerID 356 func HandleDeletePractitionerAppointment(svc dao.Service) http.Handler { 357 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 358 vars := mux.Vars(req) 359 transactionID, practitionerID, err := getTransactionIDAndPractitionerIDFromVars(vars) 360 if err != nil { 361 log.ErrorR(req, err) 362 m := models.NewMessageResponse(err.Error()) 363 utils.WriteJSONWithStatus(w, req, m, http.StatusBadRequest) 364 return 365 } 366 367 log.InfoR(req, fmt.Sprintf("start GET request for appointments resource with transaction ID: [%s] and practitioner ID: [%s]", transactionID, practitionerID)) 368 369 // Check if transaction is closed 370 isTransactionClosed, err, httpStatus := service.CheckIfTransactionClosed(transactionID, req) 371 if err != nil { 372 log.ErrorR(req, fmt.Errorf(constants.MsgErrorCheckTransactionStatus, transactionID, err)) 373 m := models.NewMessageResponse(fmt.Sprintf(constants.MsgErrorCheckTransactionStatus, transactionID, err)) 374 utils.WriteJSONWithStatus(w, req, m, httpStatus) 375 return 376 } 377 if isTransactionClosed { 378 log.ErrorR(req, fmt.Errorf(constants.MsgNoUpdateTransactionClosed, transactionID)) 379 m := models.NewMessageResponse(fmt.Sprintf(constants.MsgNoUpdateTransactionClosed, transactionID)) 380 utils.WriteJSONWithStatus(w, req, m, httpStatus) 381 return 382 } 383 384 err, statusCode := svc.DeletePractitionerAppointment(transactionID, practitionerID) 385 if err != nil { 386 log.ErrorR(req, err) 387 m := models.NewMessageResponse(err.Error()) 388 utils.WriteJSONWithStatus(w, req, m, statusCode) 389 return 390 } 391 392 w.WriteHeader(statusCode) 393 }) 394 } 395 396 func getTransactionIDAndPractitionerIDFromVars(vars map[string]string) (transactionID string, practitionerID string, err error) { 397 transactionID = utils.GetTransactionIDFromVars(vars) 398 if transactionID == "" { 399 err = fmt.Errorf("there is no Transaction ID in the URL path") 400 return 401 } 402 403 // Check for a practitioner ID in request 404 practitionerID = utils.GetPractitionerIDFromVars(vars) 405 if practitionerID == "" { 406 err = fmt.Errorf("there is no Practitioner ID in the URL path") 407 return 408 } 409 return transactionID, practitionerID, nil 410 }