From 5ca23a8e8aa94f8cb65365fbb72d506636a41a55 Mon Sep 17 00:00:00 2001 From: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:28:29 +0800 Subject: [PATCH 1/4] refactor process response Signed-off-by: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> --- .../item/item-controller-interface.go | 1 + src/goapp/controller/item/item-controller.go | 71 ++++++++++++ src/goapp/model/approvals.go | 11 -- src/goapp/model/item.go | 58 ++++++---- .../item/item-repository-interface.go | 4 + src/goapp/repository/item/item-repository.go | 101 ++++++++++++++++++ src/goapp/routes.go | 2 +- src/goapp/routes/pages/approvals/response.go | 50 --------- .../service/item/item-service-interface.go | 4 + src/goapp/service/item/item-service.go | 38 +++++++ 10 files changed, 259 insertions(+), 81 deletions(-) delete mode 100644 src/goapp/model/approvals.go diff --git a/src/goapp/controller/item/item-controller-interface.go b/src/goapp/controller/item/item-controller-interface.go index 19a8c5f..e29956a 100644 --- a/src/goapp/controller/item/item-controller-interface.go +++ b/src/goapp/controller/item/item-controller-interface.go @@ -5,4 +5,5 @@ import "net/http" type ItemController interface { GetItems(w http.ResponseWriter, r *http.Request) CreateItem(w http.ResponseWriter, r *http.Request) + ProcessResponse(w http.ResponseWriter, r *http.Request) } diff --git a/src/goapp/controller/item/item-controller.go b/src/goapp/controller/item/item-controller.go index 32c3437..abdb707 100644 --- a/src/goapp/controller/item/item-controller.go +++ b/src/goapp/controller/item/item-controller.go @@ -1,6 +1,7 @@ package item import ( + "bytes" "encoding/json" "fmt" "main/model" @@ -174,5 +175,75 @@ func (c *itemController) CreateItem(w http.ResponseWriter, r *http.Request) { return } w.Write(jsonResp) +} + +func (c *itemController) ProcessResponse(w http.ResponseWriter, r *http.Request) { + // Decode payload + var req model.ProcessResponseRequest + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // Validate payload + valid, err := c.Service.Item.ValidateItem(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if !valid { + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + + // Update item response + err = c.Service.Item.UpdateItemResponse(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Post callback + c.postCallback(req.ItemId) + // Prepare response + w.WriteHeader(http.StatusOK) +} + +func (c *itemController) postCallback(id string) { + item, err := c.Service.Item.GetItemById(id) + if err != nil { + fmt.Println("Error getting item by id: ", id) + return + } + + if item.CallbackUrl == "" { + fmt.Println("No callback url found") + return + } else { + params := model.ResponseCallback{ + ItemId: id, + IsApproved: item.IsApproved, + Remarks: item.ApproverRemarks, + ResponseDate: item.DateResponded, + RespondedBy: item.RespondedBy, + } + + jsonReq, err := json.Marshal(params) + if err != nil { + return + } + + res, err := http.Post(item.CallbackUrl, "application/json", bytes.NewBuffer(jsonReq)) + if err != nil { + fmt.Println("Error posting callback: ", err) + return + } + + isCallbackFailed := res.StatusCode != 200 + + err = c.Service.Item.UpdateItemCallback(id, isCallbackFailed) + } } diff --git a/src/goapp/model/approvals.go b/src/goapp/model/approvals.go deleted file mode 100644 index eb7785c..0000000 --- a/src/goapp/model/approvals.go +++ /dev/null @@ -1,11 +0,0 @@ -package model - -type TypRequestProcess struct { - ApplicationId string `json:"applicationId"` - ApplicationModuleId string `json:"applicationModuleId"` - ItemId string `json:"itemId"` - ApproverEmail string `json:"approverEmail"` - Remarks string `json:"remarks"` - IsApproved string `json:"isApproved"` - Username string `json:"username"` -} diff --git a/src/goapp/model/item.go b/src/goapp/model/item.go index c81de77..cf47c9e 100644 --- a/src/goapp/model/item.go +++ b/src/goapp/model/item.go @@ -1,25 +1,27 @@ package model type Item struct { - Id string `json:"id"` - Application string `json:"application"` - ApproverRemarks string `json:"approverRemarks"` - Body string `json:"body"` - Created string `json:"created"` - DateResponded string `json:"dateResponded"` - DateSent string `json:"dateSent"` - IsApproved bool `json:"isApproved"` - Module string `json:"module"` - Subject string `json:"subject"` - ApproveText string `json:"approveText"` - RejectText string `json:"rejectText"` - ApproveUrl string `json:"approveUrl"` - RejectUrl string `json:"rejectUrl"` - AllowReassign bool `json:"allowReassign"` - AllowReassignUrl string `json:"allowReassignUrl"` - RespondedBy string `json:"respondedBy"` - Approvers []string `json:"approvers"` - RequestedBy string `json:"requestedBy"` + Id string `json:"id"` + Application string `json:"application"` + ApproverRemarks string `json:"approverRemarks"` + Body string `json:"body"` + Created string `json:"created"` + DateResponded string `json:"dateResponded"` + DateSent string `json:"dateSent"` + IsApproved bool `json:"isApproved"` + Module string `json:"module"` + Subject string `json:"subject"` + ApproveText string `json:"approveText"` + CallbackUrl string `json:"callbackUrl"` + ReassignCallbackUrl string `json:"reassignCallbackUrl"` + RejectText string `json:"rejectText"` + ApproveUrl string `json:"approveUrl"` + RejectUrl string `json:"rejectUrl"` + AllowReassign bool `json:"allowReassign"` + AllowReassignUrl string `json:"allowReassignUrl"` + RespondedBy string `json:"respondedBy"` + Approvers []string `json:"approvers"` + RequestedBy string `json:"requestedBy"` } type ItemOptions struct { @@ -69,3 +71,21 @@ type ApprovalRequestApprover struct { ItemId string ApproverEmail string } + +type ProcessResponseRequest struct { + ApplicationId string `json:"applicationId"` + ApplicationModuleId string `json:"applicationModuleId"` + ItemId string `json:"itemId"` + ApproverEmail string `json:"approverEmail"` + Remarks string `json:"remarks"` + IsApproved string `json:"isApproved"` + Username string `json:"username"` +} + +type ResponseCallback struct { + ItemId string `json:"itemId"` + IsApproved bool `json:"isApproved"` + Remarks string `json:"remarks"` + ResponseDate string `json:"responseDate"` + RespondedBy string `json:"respondedBy"` +} diff --git a/src/goapp/repository/item/item-repository-interface.go b/src/goapp/repository/item/item-repository-interface.go index aec7cba..8487512 100644 --- a/src/goapp/repository/item/item-repository-interface.go +++ b/src/goapp/repository/item/item-repository-interface.go @@ -5,8 +5,12 @@ import ( ) type ItemRepository interface { + GetItemById(id string) (*model.Item, error) GetItemsBy(itemOptions model.ItemOptions) ([]model.Item, error) GetTotalItemsBy(itemOptions model.ItemOptions) (int, error) InsertItem(appModuleId, subject, body, requesterEmail string) (string, error) + UpdateItemCallback(id string, isCallbackFailed bool) error UpdateItemDateSent(id string) error + UpdateItemResponse(id, remarks, email string, isApproved bool) error + ValidateItem(appId, appModuleId, itemId, email string) (bool, error) } diff --git a/src/goapp/repository/item/item-repository.go b/src/goapp/repository/item/item-repository.go index d5b571a..442241b 100644 --- a/src/goapp/repository/item/item-repository.go +++ b/src/goapp/repository/item/item-repository.go @@ -19,6 +19,64 @@ func NewItemRepository(db db.Database) ItemRepository { } } +func (r *itemRepository) GetItemById(id string) (*model.Item, error) { + row, err := r.Query("PR_Items_Select_ById", sql.Named("Id", id)) + if err != nil { + return nil, err + } + + result, err := r.RowsToMap(row) + if err != nil { + return nil, err + } + + item := model.Item{ + Id: id, + Application: result[0]["Application"].(string), + Module: result[0]["Module"].(string), + ApproveText: result[0]["ApproveText"].(string), + RejectText: result[0]["RejectText"].(string), + } + + if result[0]["ApproverRemarks"] != nil { + item.ApproverRemarks = result[0]["ApproverRemarks"].(string) + } + + if result[0]["Body"] != nil { + item.Body = result[0]["Body"].(string) + } + + if result[0]["DateResponded"] != nil { + item.DateResponded = result[0]["DateResponded"].(time.Time).String() + } + + if result[0]["DateSent"] != nil { + item.DateSent = result[0]["DateSent"].(time.Time).String() + } + + if result[0]["IsApproved"] != nil { + item.IsApproved = result[0]["IsApproved"].(bool) + } + + if result[0]["Subject"] != nil { + item.Subject = result[0]["Subject"].(string) + } + + if result[0]["CallbackUrl"] != nil { + item.CallbackUrl = result[0]["CallbackUrl"].(string) + } + + if result[0]["ReassignCallbackUrl"] != nil { + item.ReassignCallbackUrl = result[0]["ReassignCallbackUrl"].(string) + } + + if result[0]["RespondedBy"] != nil { + item.RespondedBy = result[0]["RespondedBy"].(string) + } + + return &item, nil +} + func (r *itemRepository) GetItemsBy(itemOptions model.ItemOptions) ([]model.Item, error) { var params []interface{} @@ -161,6 +219,17 @@ func (r *itemRepository) InsertItem(appModuleId, subject, body, requesterEmail s return resultItem[0]["Id"].(string), nil } +func (r *itemRepository) UpdateItemCallback(id string, isCallbackFailed bool) error { + _, err := r.Query("PR_Items_Update_Callback", + sql.Named("ItemId", id), + sql.Named("IsCallbackFailed", isCallbackFailed), + ) + if err != nil { + return err + } + return nil +} + func (r *itemRepository) UpdateItemDateSent(id string) error { _, err := r.Query("PR_Items_Update_DateSent", sql.Named("Id", id), @@ -172,3 +241,35 @@ func (r *itemRepository) UpdateItemDateSent(id string) error { return nil } + +func (r *itemRepository) UpdateItemResponse(id, remarks, email string, isApproved bool) error { + _, err := r.Query("PR_Items_Update_Response", + sql.Named("Id", id), + sql.Named("ApproverRemarks", remarks), + sql.Named("Username", email), + sql.Named("IsApproved", isApproved), + ) + if err != nil { + return err + } + return nil +} + +func (r *itemRepository) ValidateItem(appId, appModuleId, itemId, email string) (bool, error) { + row, err := r.Query("PR_Items_IsValid", + sql.Named("ApplicationId", appId), + sql.Named("ApplicationModuleId", appModuleId), + sql.Named("ItemId", itemId), + sql.Named("ApproverEmail", email), + ) + if err != nil { + return false, err + } + + result, err := r.RowsToMap(row) + if err != nil { + return false, err + } + + return result[0]["IsValid"].(bool), nil +} diff --git a/src/goapp/routes.go b/src/goapp/routes.go index 411daaf..d9da9c1 100644 --- a/src/goapp/routes.go +++ b/src/goapp/routes.go @@ -24,7 +24,7 @@ func setPageRoutes() { func setApiRoutes() { httpRouter.GET("/api/request/types", m.Chain(rtApi.GetRequestTypes, m.AzureAuth())) httpRouter.POST("/api/request", ctrl.Item.CreateItem) - httpRouter.POST("/api/process", rtApprovals.ProcessResponseHandler) + httpRouter.POST("/api/process", ctrl.Item.ProcessResponse) httpRouter.GET("/api/items/type/{type:[0-2]+}/status/{status:[0-3]+}", m.Chain(ctrl.Item.GetItems, m.AzureAuth())) httpRouter.GET("/api/search/users/{search}", m.Chain(rtApi.SearchUserFromActiveDirectory, m.AzureAuth())) httpRouter.GET("/api/responsereassignedapi/{itemGuid}/{approver}/{ApplicationId}/{ApplicationModuleId}/{itemId}/{ApproveText}/{RejectText}", m.Chain(rtApprovals.ReAssignApproverHandler, m.AzureAuth())) diff --git a/src/goapp/routes/pages/approvals/response.go b/src/goapp/routes/pages/approvals/response.go index 7a66c4c..0d5e73d 100644 --- a/src/goapp/routes/pages/approvals/response.go +++ b/src/goapp/routes/pages/approvals/response.go @@ -4,13 +4,11 @@ import ( "bytes" "encoding/json" "fmt" - "main/model" session "main/pkg/session" "main/pkg/sql" template "main/pkg/template" "net/http" "os" - "strconv" "time" "github.com/gorilla/mux" @@ -181,54 +179,6 @@ func ResponseHandler(w http.ResponseWriter, r *http.Request) { } } -func ProcessResponseHandler(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "POST": - // Decode payload - var req model.TypRequestProcess - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - - return - } - - db := connectSql() - defer db.Close() - - // Validate payload - params := make(map[string]interface{}) - params["ApplicationId"] = req.ApplicationId - params["ApplicationModuleId"] = req.ApplicationModuleId - params["ItemId"] = req.ItemId - params["ApproverEmail"] = req.ApproverEmail - verification, err := db.ExecuteStoredProcedureWithResult("PR_Items_IsValid", params) - handleErrorReturn(w, err) - authReq := true - authReq = session.IsAuthRequired(req.ApplicationModuleId) - if !authReq { - verification[0]["IsValid"] = "1" - } - - if verification[0]["IsValid"] == "1" { - for k := range params { - delete(params, k) - } - isApproved, _ := strconv.ParseBool(req.IsApproved) - params["Id"] = req.ItemId - params["IsApproved"] = isApproved - params["ApproverRemarks"] = req.Remarks - params["Username"] = req.ApproverEmail - _, err := db.ExecuteStoredProcedure("PR_Items_Update_Response", params) - handleErrorReturn(w, err) - postCallback(req.ItemId) - return - } else { - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } - } -} - func ProcessFailedCallbacks() { db := connectSql() defer db.Close() diff --git a/src/goapp/service/item/item-service-interface.go b/src/goapp/service/item/item-service-interface.go index 43b0f18..26e78ae 100644 --- a/src/goapp/service/item/item-service-interface.go +++ b/src/goapp/service/item/item-service-interface.go @@ -5,7 +5,11 @@ import ( ) type ItemService interface { + GetItemById(id string) (*model.Item, error) GetAll(itemOptions model.ItemOptions) (model.Response, error) InsertItem(item model.ItemInsertRequest) (string, error) + UpdateItemCallback(itemId string, isCallbackFailed bool) error UpdateItemDateSent(itemId string) error + UpdateItemResponse(req model.ProcessResponseRequest) error + ValidateItem(req model.ProcessResponseRequest) (bool, error) } diff --git a/src/goapp/service/item/item-service.go b/src/goapp/service/item/item-service.go index 76f8680..21f7cf8 100644 --- a/src/goapp/service/item/item-service.go +++ b/src/goapp/service/item/item-service.go @@ -5,6 +5,7 @@ import ( "main/config" "main/model" "main/repository" + "strconv" "sync" ) @@ -20,6 +21,14 @@ func NewItemService(repo *repository.Repository, configManager config.ConfigMana } } +func (s *itemService) GetItemById(id string) (*model.Item, error) { + item, err := s.Repository.Item.GetItemById(id) + if err != nil { + return nil, err + } + return item, nil +} + func (s *itemService) GetAll(itemOptions model.ItemOptions) (model.Response, error) { var result model.Response @@ -74,6 +83,14 @@ func (s *itemService) InsertItem(item model.ItemInsertRequest) (string, error) { return id, nil } +func (s *itemService) UpdateItemCallback(itemId string, isCallbackFailed bool) error { + err := s.Repository.Item.UpdateItemCallback(itemId, isCallbackFailed) + if err != nil { + return err + } + return nil +} + func (s *itemService) UpdateItemDateSent(itemId string) error { err := s.Repository.Item.UpdateItemDateSent(itemId) if err != nil { @@ -105,3 +122,24 @@ func (s *itemService) removeEnterpriseOwnersInApprovers(approvers []string) []st return newApprovers } + +func (s *itemService) UpdateItemResponse(req model.ProcessResponseRequest) error { + isApproved, err := strconv.ParseBool(req.IsApproved) + if err != nil { + return err + } + + err = s.Repository.Item.UpdateItemResponse(req.ItemId, req.Remarks, req.ApproverEmail, isApproved) + if err != nil { + return err + } + return nil +} + +func (s *itemService) ValidateItem(req model.ProcessResponseRequest) (bool, error) { + isValid, err := s.Repository.Item.ValidateItem(req.ApplicationId, req.ApplicationModuleId, req.ItemId, req.ApproverEmail) + if err != nil { + return false, err + } + return isValid, nil +} From ede20a0165fec008c25e2b5e461706481cd2c122 Mon Sep 17 00:00:00 2001 From: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> Date: Fri, 27 Sep 2024 18:05:33 +0800 Subject: [PATCH 2/4] fix error Signed-off-by: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> --- src/goapp/repository/item/item-repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/goapp/repository/item/item-repository.go b/src/goapp/repository/item/item-repository.go index 442241b..d9a5292 100644 --- a/src/goapp/repository/item/item-repository.go +++ b/src/goapp/repository/item/item-repository.go @@ -271,5 +271,5 @@ func (r *itemRepository) ValidateItem(appId, appModuleId, itemId, email string) return false, err } - return result[0]["IsValid"].(bool), nil + return result[0]["IsValid"] == "1", nil } From 480f3f68302a4c08b2e90b8e7dd116dfecb8c720 Mon Sep 17 00:00:00 2001 From: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:52:15 +0800 Subject: [PATCH 3/4] format date responded Signed-off-by: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> --- src/goapp/controller/item/item-controller.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/goapp/controller/item/item-controller.go b/src/goapp/controller/item/item-controller.go index abdb707..9bf36de 100644 --- a/src/goapp/controller/item/item-controller.go +++ b/src/goapp/controller/item/item-controller.go @@ -9,6 +9,7 @@ import ( "main/service" "net/http" "strconv" + "time" "github.com/gorilla/mux" ) @@ -223,11 +224,21 @@ func (c *itemController) postCallback(id string) { fmt.Println("No callback url found") return } else { + // Parse the datetime string into a time.Time object + responseDate, err := time.Parse(time.RFC3339, item.DateResponded) + if err != nil { + fmt.Printf("invalid datetime format: %v", err) + return + } + + // Format the time.Time object into a string that the database can recognize + formattedResponseDate := responseDate.Format("2006-01-02 15:04:05") + params := model.ResponseCallback{ ItemId: id, IsApproved: item.IsApproved, Remarks: item.ApproverRemarks, - ResponseDate: item.DateResponded, + ResponseDate: formattedResponseDate, RespondedBy: item.RespondedBy, } From e9f7f5f2bed082342718869882da54b69edb68bb Mon Sep 17 00:00:00 2001 From: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:28:29 +0800 Subject: [PATCH 4/4] format time Signed-off-by: Ismael Ibuan <102030576+iibuan@users.noreply.github.com> --- src/goapp/controller/item/item-controller.go | 13 +------------ src/goapp/repository/item/item-repository.go | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/goapp/controller/item/item-controller.go b/src/goapp/controller/item/item-controller.go index 9bf36de..abdb707 100644 --- a/src/goapp/controller/item/item-controller.go +++ b/src/goapp/controller/item/item-controller.go @@ -9,7 +9,6 @@ import ( "main/service" "net/http" "strconv" - "time" "github.com/gorilla/mux" ) @@ -224,21 +223,11 @@ func (c *itemController) postCallback(id string) { fmt.Println("No callback url found") return } else { - // Parse the datetime string into a time.Time object - responseDate, err := time.Parse(time.RFC3339, item.DateResponded) - if err != nil { - fmt.Printf("invalid datetime format: %v", err) - return - } - - // Format the time.Time object into a string that the database can recognize - formattedResponseDate := responseDate.Format("2006-01-02 15:04:05") - params := model.ResponseCallback{ ItemId: id, IsApproved: item.IsApproved, Remarks: item.ApproverRemarks, - ResponseDate: formattedResponseDate, + ResponseDate: item.DateResponded, RespondedBy: item.RespondedBy, } diff --git a/src/goapp/repository/item/item-repository.go b/src/goapp/repository/item/item-repository.go index d9a5292..c1ac5ae 100644 --- a/src/goapp/repository/item/item-repository.go +++ b/src/goapp/repository/item/item-repository.go @@ -47,7 +47,7 @@ func (r *itemRepository) GetItemById(id string) (*model.Item, error) { } if result[0]["DateResponded"] != nil { - item.DateResponded = result[0]["DateResponded"].(time.Time).String() + item.DateResponded = result[0]["DateResponded"].(time.Time).Format("2006-01-02T15:04:05.000Z") } if result[0]["DateSent"] != nil {