diff --git a/src/goapp/controller/item/item-controller-dto.go b/src/goapp/controller/item/item-controller-dto.go index 5e760de..f0734f8 100644 --- a/src/goapp/controller/item/item-controller-dto.go +++ b/src/goapp/controller/item/item-controller-dto.go @@ -1,5 +1,7 @@ package item +import "main/model" + type ReassignItemCallback struct { Id string `json:"Id"` ApproverEmail string `json:"ApproverEmail"` @@ -9,3 +11,14 @@ type ReassignItemCallback struct { ApproveText string `json:"ApproveText"` RejectText string `json:"RejectText"` } + +type RespondePageData struct { + ApplicationId string + ApplicationModuleId string + ItemId string + ApproverEmail string + IsApproved string + Data model.Item + RequireRemarks bool + Response string +} diff --git a/src/goapp/controller/item/item-controller-interface.go b/src/goapp/controller/item/item-controller-interface.go index 82633c9..3df1d01 100644 --- a/src/goapp/controller/item/item-controller-interface.go +++ b/src/goapp/controller/item/item-controller-interface.go @@ -12,4 +12,5 @@ type ItemController interface { type ItemPageController interface { MyRequests(w http.ResponseWriter, r *http.Request) MyApprovals(w http.ResponseWriter, r *http.Request) + RespondToItem(w http.ResponseWriter, r *http.Request) } diff --git a/src/goapp/controller/item/item-page-controller.go b/src/goapp/controller/item/item-page-controller.go index fa8657a..ae4a103 100644 --- a/src/goapp/controller/item/item-page-controller.go +++ b/src/goapp/controller/item/item-page-controller.go @@ -8,6 +8,8 @@ import ( "main/pkg/session" "main/service" "net/http" + + "github.com/gorilla/mux" ) type itemPageController struct { @@ -99,3 +101,81 @@ func (c *itemPageController) MyApprovals(w http.ResponseWriter, r *http.Request) http.Error(w, err.Error(), http.StatusInternalServerError) } } + +func (c *itemPageController) RespondToItem(w http.ResponseWriter, r *http.Request) { + session, _ := session.Store.Get(r, "auth-session") + + var profile map[string]interface{} + u := session.Values["profile"] + profile, ok := u.(map[string]interface{}) + if !ok { + http.Error(w, "Error getting user data", http.StatusInternalServerError) + return + } + user := model.AzureUser{ + Name: profile["name"].(string), + Email: profile["preferred_username"].(string), + } + + params := mux.Vars(r) + + itemIsAuthorized, err := c.Service.Item.ItemIsAuthorized( + params["appGuid"], + params["appModuleGuid"], + params["itemGuid"], + user.Email, + ) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if !itemIsAuthorized.IsAuthorized { + t, d := c.Service.Template.UseTemplate("Unauthorized", r.URL.Path, user, nil) + err = t.Execute(w, d) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } else { + if itemIsAuthorized.IsApproved != nil { + var text string + if itemIsAuthorized.IsApproved.Value { + text = "approved" + } else { + text = "rejected" + } + + data := RespondePageData{ + Response: text, + } + + t, d := c.Service.Template.UseTemplate("already-processed", r.URL.Path, user, data) + err = t.Execute(w, d) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } else { + item, err := c.Service.Item.GetItemById(params["itemGuid"]) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + data := RespondePageData{ + ApplicationId: params["appGuid"], + ApplicationModuleId: params["appModuleGuid"], + ItemId: params["itemGuid"], + ApproverEmail: user.Email, + IsApproved: params["isApproved"], + Data: *item, + RequireRemarks: itemIsAuthorized.RequireRemarks, + } + + t, d := c.Service.Template.UseTemplate("response", r.URL.Path, user, data) + err = t.Execute(w, d) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } + } +} diff --git a/src/goapp/infrastructure/database/database.go b/src/goapp/infrastructure/database/database.go index ed3f706..ed861e3 100644 --- a/src/goapp/infrastructure/database/database.go +++ b/src/goapp/infrastructure/database/database.go @@ -5,40 +5,44 @@ import ( "database/sql" "fmt" "main/config" + "time" _ "github.com/microsoft/go-mssqldb" ) type Database struct { - connString string + db *sql.DB } func NewDatabase(config config.ConfigManager) Database { - fmt.Println("ConnectDb New") connectionString := config.GetDatabaseConnectionString() - return Database{ - connString: connectionString, - } -} -func (d *Database) Connect() (*sql.DB, error) { - conn, err := sql.Open("sqlserver", d.connString) + conn, err := sql.Open("sqlserver", connectionString) if err != nil { - return nil, err + fmt.Println(err.Error()) + return Database{db: nil} } - return conn, nil -} + conn.SetMaxOpenConns(10) + conn.SetMaxIdleConns(10) + conn.SetConnMaxLifetime(5 * time.Minute) -func (d *Database) Query(query string, args ...any) (*sql.Rows, error) { - conn, err := d.Connect() + err = conn.Ping() if err != nil { - return nil, err + fmt.Println(err.Error()) + return Database{db: nil} + } + + fmt.Println("Database connection established and configured.") + + return Database{ + db: conn, } - defer conn.Close() +} +func (d *Database) Query(query string, args ...any) (*sql.Rows, error) { ctx := context.Background() - rows, err := conn.QueryContext(ctx, query, args...) + rows, err := d.db.QueryContext(ctx, query, args...) if err != nil { return nil, err } @@ -46,29 +50,18 @@ func (d *Database) Query(query string, args ...any) (*sql.Rows, error) { } func (d *Database) QueryRow(query string, args ...any) (*sql.Row, error) { - conn, err := d.Connect() - if err != nil { - return nil, err - } - defer conn.Close() - ctx := context.Background() - row := conn.QueryRowContext(ctx, query, args...) + row := d.db.QueryRowContext(ctx, query, args...) if row == nil { - err = fmt.Errorf("QueryRowContext returned nil") + err := fmt.Errorf("QueryRowContext returned nil") + return nil, err } return row, nil } func (d *Database) Execute(query string, args ...any) error { - conn, err := d.Connect() - if err != nil { - return err - } - defer conn.Close() - ctx := context.Background() - _, err = conn.ExecContext(ctx, query, args...) + _, err := d.db.ExecContext(ctx, query, args...) if err != nil { return err } diff --git a/src/goapp/model/item.go b/src/goapp/model/item.go index cf47c9e..5cba394 100644 --- a/src/goapp/model/item.go +++ b/src/goapp/model/item.go @@ -89,3 +89,13 @@ type ResponseCallback struct { ResponseDate string `json:"responseDate"` RespondedBy string `json:"respondedBy"` } + +type ItemIsAuthorized struct { + IsAuthorized bool `json:"isAuthorized"` + IsApproved *NullBool `json:"isApproved"` + RequireRemarks bool `json:"requireRemarks"` +} + +type NullBool struct { + Value bool +} diff --git a/src/goapp/model/template.go b/src/goapp/model/template.go index dfec85b..1d5f36c 100644 --- a/src/goapp/model/template.go +++ b/src/goapp/model/template.go @@ -3,7 +3,7 @@ package model type MasterPageData struct { Header Headers Profile AzureUser - Content interface{} + Content interface{} `json:"content"` Footers []Footer OrganizationName string } diff --git a/src/goapp/public/css/output.css b/src/goapp/public/css/output.css index cdceecd..2c929e6 100644 --- a/src/goapp/public/css/output.css +++ b/src/goapp/public/css/output.css @@ -1847,18 +1847,14 @@ select { top: 50%; } -.top-5 { - top: 1.25rem; +.top-8 { + top: 2rem; } .top-full { top: 100%; } -.top-8 { - top: 2rem; -} - .isolate { isolation: isolate; } diff --git a/src/goapp/repository/app-module/app-module-repository.go b/src/goapp/repository/app-module/app-module-repository.go index 10bb996..31aa6c2 100644 --- a/src/goapp/repository/app-module/app-module-repository.go +++ b/src/goapp/repository/app-module/app-module-repository.go @@ -24,6 +24,7 @@ func (r *applicationModuleRepository) GetApplicationModuleByIdAndApplicationId(a if err != nil { return nil, err } + defer rowApplicationModule.Close() applicationModule, err := r.RowsToMap(rowApplicationModule) if err != nil { @@ -61,6 +62,7 @@ func (r *applicationModuleRepository) GetAll() ([]model.ApplicationModule, error if err != nil { return nil, err } + defer rowApplicationModules.Close() applicationModules, err := r.RowsToMap(rowApplicationModules) if err != nil { diff --git a/src/goapp/repository/approval-request-approver/approval-request-approver-repository.go b/src/goapp/repository/approval-request-approver/approval-request-approver-repository.go index 3199f50..fbe9742 100644 --- a/src/goapp/repository/approval-request-approver/approval-request-approver-repository.go +++ b/src/goapp/repository/approval-request-approver/approval-request-approver-repository.go @@ -17,7 +17,7 @@ func NewApprovalRequestApproverRepository(db *db.Database) ApprovalRequestApprov } func (r *approvalRequestApproverRepository) InsertApprovalRequestApprover(approver model.ApprovalRequestApprover) error { - _, err := r.Query("PR_ApprovalRequestApprovers_Insert", + row, err := r.Query("PR_ApprovalRequestApprovers_Insert", sql.Named("ItemId", approver.ItemId), sql.Named("ApproverEmail", approver.ApproverEmail), ) @@ -25,6 +25,7 @@ func (r *approvalRequestApproverRepository) InsertApprovalRequestApprover(approv if err != nil { return err } + defer row.Close() return nil } @@ -35,6 +36,7 @@ func (r *approvalRequestApproverRepository) GetApproversByItemId(itemId string) if err != nil { return nil, err } + defer rowApprovers.Close() approvers, err := r.RowsToMap(rowApprovers) if err != nil { diff --git a/src/goapp/repository/item/item-repository-interface.go b/src/goapp/repository/item/item-repository-interface.go index 80e6231..60ddf81 100644 --- a/src/goapp/repository/item/item-repository-interface.go +++ b/src/goapp/repository/item/item-repository-interface.go @@ -9,6 +9,7 @@ type ItemRepository interface { GetItemsBy(itemOptions model.ItemOptions) ([]model.Item, error) GetTotalItemsBy(itemOptions model.ItemOptions) (int, error) InsertItem(appModuleId, subject, body, requesterEmail string) (string, error) + ItemIsAuthorized(appId, appModuleId, itemId, approverEmail string) (*model.ItemIsAuthorized, error) UpdateItemApproverEmail(id, approverEmail, username string) error UpdateItemCallback(id string, isCallbackFailed bool) error UpdateItemDateSent(id string) error diff --git a/src/goapp/repository/item/item-repository.go b/src/goapp/repository/item/item-repository.go index 0f1c3e2..6b12d3b 100644 --- a/src/goapp/repository/item/item-repository.go +++ b/src/goapp/repository/item/item-repository.go @@ -24,6 +24,7 @@ func (r *itemRepository) GetItemById(id string) (*model.Item, error) { if err != nil { return nil, err } + defer row.Close() result, err := r.RowsToMap(row) if err != nil { @@ -102,6 +103,7 @@ func (r *itemRepository) GetItemsBy(itemOptions model.ItemOptions) ([]model.Item if err != nil { return []model.Item{}, err } + defer resList.Close() result, err := r.RowsToMap(resList) if err != nil { @@ -185,6 +187,7 @@ func (r *itemRepository) GetTotalItemsBy(itemOptions model.ItemOptions) (int, er if err != nil { return 0, err } + defer rowTotal.Close() resultTotal, err := r.RowsToMap(rowTotal) if err != nil { @@ -210,6 +213,7 @@ func (r *itemRepository) InsertItem(appModuleId, subject, body, requesterEmail s if err != nil { return "", err } + defer rowItem.Close() resultItem, err := r.RowsToMap(rowItem) if err != nil { @@ -220,7 +224,7 @@ func (r *itemRepository) InsertItem(appModuleId, subject, body, requesterEmail s } func (r *itemRepository) UpdateItemApproverEmail(id, approverEmail, username string) error { - _, err := r.Query("PR_Items_Update_ApproverEmail", + row, err := r.Query("PR_Items_Update_ApproverEmail", sql.Named("Id", id), sql.Named("ApproverEmail", approverEmail), sql.Named("Username", username), @@ -228,34 +232,70 @@ func (r *itemRepository) UpdateItemApproverEmail(id, approverEmail, username str if err != nil { return err } + defer row.Close() return nil } +func (r *itemRepository) ItemIsAuthorized(appId, appModuleId, itemId, approverEmail string) (*model.ItemIsAuthorized, error) { + row, err := r.Query("PR_Items_IsAuthorized", + sql.Named("ApplicationId", appId), + sql.Named("ApplicationModuleId", appModuleId), + sql.Named("ItemId", itemId), + sql.Named("ApproverEmail", approverEmail), + ) + if err != nil { + return nil, err + } + defer row.Close() + + result, err := r.RowsToMap(row) + if err != nil { + return nil, err + } + + i := model.ItemIsAuthorized{ + IsAuthorized: result[0]["IsAuthorized"] == "1", + } + + if result[0]["IsApproved"] != nil { + i.IsApproved = &model.NullBool{Value: result[0]["IsApproved"].(bool)} + } else { + i.IsApproved = nil + } + + if result[0]["RequireRemarks"] != nil { + i.RequireRemarks = result[0]["RequireRemarks"].(bool) + } + + return &i, nil +} func (r *itemRepository) UpdateItemCallback(id string, isCallbackFailed bool) error { - _, err := r.Query("PR_Items_Update_Callback", + row, err := r.Query("PR_Items_Update_Callback", sql.Named("ItemId", id), sql.Named("IsCallbackFailed", isCallbackFailed), ) if err != nil { return err } + defer row.Close() return nil } func (r *itemRepository) UpdateItemDateSent(id string) error { - _, err := r.Query("PR_Items_Update_DateSent", + row, err := r.Query("PR_Items_Update_DateSent", sql.Named("Id", id), ) if err != nil { return err } + defer row.Close() return nil } func (r *itemRepository) UpdateItemResponse(id, remarks, email string, isApproved bool) error { - _, err := r.Query("PR_Items_Update_Response", + row, err := r.Query("PR_Items_Update_Response", sql.Named("Id", id), sql.Named("ApproverRemarks", remarks), sql.Named("Username", email), @@ -264,6 +304,7 @@ func (r *itemRepository) UpdateItemResponse(id, remarks, email string, isApprove if err != nil { return err } + defer row.Close() return nil } @@ -277,6 +318,7 @@ func (r *itemRepository) ValidateItem(appId, appModuleId, itemId, email string) if err != nil { return false, err } + defer row.Close() result, err := r.RowsToMap(row) if err != nil { diff --git a/src/goapp/routes.go b/src/goapp/routes.go index 2aad4f4..29dc4fe 100644 --- a/src/goapp/routes.go +++ b/src/goapp/routes.go @@ -11,7 +11,7 @@ import ( func setPageRoutes() { httpRouter.GET("/", m.Chain(ctrl.ItemPage.MyRequests, m.AzureAuth())) httpRouter.GET("/myapprovals", m.Chain(ctrl.ItemPage.MyApprovals, m.AzureAuth())) - httpRouter.GET("/response/{appGuid}/{appModuleGuid}/{itemGuid}/{isApproved}", m.Chain(rtApprovals.ResponseHandler, m.AzureAuth())) + httpRouter.GET("/response/{appGuid}/{appModuleGuid}/{itemGuid}/{isApproved}", m.Chain(ctrl.ItemPage.RespondToItem, m.AzureAuth())) httpRouter.GET("/responsereassigned/{appGuid}/{appModuleGuid}/{itemGuid}/{isApproved}/{ApproveText}/{RejectText}", m.Chain(rtApprovals.ResponseReassignedeHandler, m.AzureAuth())) httpRouter.GET("/loginredirect", rtPages.LoginRedirectHandler) diff --git a/src/goapp/routes/pages/approvals/response.go b/src/goapp/routes/pages/approvals/response.go index 0d5e73d..0ab7119 100644 --- a/src/goapp/routes/pages/approvals/response.go +++ b/src/goapp/routes/pages/approvals/response.go @@ -108,76 +108,6 @@ func ResponseReassignedeHandler(w http.ResponseWriter, r *http.Request) { } } -func ResponseHandler(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "GET": - var username string - sessionaz, _ := session.Store.Get(r, "auth-session") - iprofile := sessionaz.Values["profile"] - - if iprofile != nil { - profile := iprofile.(map[string]interface{}) - username = profile["preferred_username"].(string) - } - params := mux.Vars(r) - - appGuid := params["appGuid"] - appModuleGuid := params["appModuleGuid"] - itemGuid := params["itemGuid"] - isApproved := params["isApproved"] - - sqlParamsIsAuth := map[string]interface{}{ - "ApplicationId": appGuid, - "ApplicationModuleId": appModuleGuid, - "ItemId": itemGuid, - "ApproverEmail": username, - } - - sqlParamsItems := map[string]interface{}{ - "Id": itemGuid, - } - - db := connectSql() - defer db.Close() - resIsAuth, err := db.ExecuteStoredProcedureWithResult("PR_RESPONSE_IsAuthorized", sqlParamsIsAuth) - handleErrorReturn(w, err) - - isAuth := resIsAuth[0]["IsAuthorized"] - if isAuth == "0" { - template.UseTemplate(&w, r, "Unauthorized", nil) - } else { - isProcessed := resIsAuth[0]["IsApproved"] - if isProcessed != nil { - var text string - if isProcessed == true { - text = "approved" - } else { - text = "rejected" - } - data := map[string]interface{}{ - "response": text, - } - template.UseTemplate(&w, r, "AlreadyProcessed", data) - } else { - resItems, err := db.ExecuteStoredProcedureWithResult("PR_Items_Select_ById", sqlParamsItems) - - handleErrorReturn(w, err) - requireRemarks := resIsAuth[0]["RequireRemarks"] - data := map[string]interface{}{ - "ApplicationId": appGuid, - "ApplicationModuleId": appModuleGuid, - "ItemId": itemGuid, - "ApproverEmail": username, - "IsApproved": isApproved, - "Data": resItems[0], - "RequireRemarks": requireRemarks, - } - template.UseTemplate(&w, r, "response", data) - } - - } - } -} func ProcessFailedCallbacks() { db := connectSql() diff --git a/src/goapp/service/item/item-service-interface.go b/src/goapp/service/item/item-service-interface.go index 92e3da0..da2a83b 100644 --- a/src/goapp/service/item/item-service-interface.go +++ b/src/goapp/service/item/item-service-interface.go @@ -8,6 +8,7 @@ type ItemService interface { GetItemById(id string) (*model.Item, error) GetAll(itemOptions model.ItemOptions) (model.Response, error) InsertItem(item model.ItemInsertRequest) (string, error) + ItemIsAuthorized(appId, appModuleId, itemId, approverEmail string) (*model.ItemIsAuthorized, error) UpdateItemApproverEmail(itemId, approverEmail, username string) error UpdateItemCallback(itemId string, isCallbackFailed bool) error UpdateItemDateSent(itemId string) error diff --git a/src/goapp/service/item/item-service.go b/src/goapp/service/item/item-service.go index 1658c9c..5c368dd 100644 --- a/src/goapp/service/item/item-service.go +++ b/src/goapp/service/item/item-service.go @@ -43,7 +43,7 @@ func (s *itemService) GetAll(itemOptions model.ItemOptions) (model.Response, err } var wg sync.WaitGroup - maxGoroutines := 10 + maxGoroutines := 2 guard := make(chan struct{}, maxGoroutines) for i := range data { @@ -83,6 +83,14 @@ func (s *itemService) InsertItem(item model.ItemInsertRequest) (string, error) { return id, nil } +func (s *itemService) ItemIsAuthorized(appId, appModuleId, itemId, approverEmail string) (*model.ItemIsAuthorized, error) { + itemIsAuthorized, err := s.Repository.Item.ItemIsAuthorized(appId, appModuleId, itemId, approverEmail) + if err != nil { + return nil, err + } + return itemIsAuthorized, nil +} + func (s *itemService) UpdateItemApproverEmail(itemId, approverEmail, username string) error { err := s.Repository.Item.UpdateItemApproverEmail(itemId, approverEmail, username) if err != nil { diff --git a/src/goapp/templates/AlreadyProcessed.html b/src/goapp/templates/already-processed.html similarity index 93% rename from src/goapp/templates/AlreadyProcessed.html rename to src/goapp/templates/already-processed.html index e8a48c8..0048c4a 100644 --- a/src/goapp/templates/AlreadyProcessed.html +++ b/src/goapp/templates/already-processed.html @@ -4,7 +4,7 @@

Processed

-

You have already {{.response}} this item.

+

You have already {{.Response}} this item.

diff --git a/src/goapp/templates/myapprovals.html b/src/goapp/templates/myapprovals.html index 0469e1b..d169904 100644 --- a/src/goapp/templates/myapprovals.html +++ b/src/goapp/templates/myapprovals.html @@ -283,7 +283,7 @@ } async function pendingCallback(e){ - return await getItemsBy(1, 0, e.other.requestType.id, '', e.filter, e.page, e.search) + return await getItemsBy(1, 0, e.other.requestType.id, e.other.organization, e.filter, e.page, e.search) } //CLOSED REQUEST diff --git a/src/goapp/templates/myrequests.html b/src/goapp/templates/myrequests.html index f209cb5..0d81ec7 100644 --- a/src/goapp/templates/myrequests.html +++ b/src/goapp/templates/myrequests.html @@ -219,7 +219,7 @@ } async function pendingCallback(e){ - return await getItemsBy(0, 0, e.other.requestType.id, '', e.filter, e.page, e.search) + return await getItemsBy(0, 0, e.other.requestType.id, e.other.organization, e.filter, e.page, e.search) } //CLOSED REQUEST diff --git a/src/goapp/templates/response.html b/src/goapp/templates/response.html index 7cd427e..06d638a 100644 --- a/src/goapp/templates/response.html +++ b/src/goapp/templates/response.html @@ -1,76 +1,194 @@ {{ define "content" }}
-

{{.Data.Subject}}

- - +

{{.Data.Subject}}

+ + -