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  }