From 683ddd3ef297923888464b6ece5f2490d78b0866 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Mon, 10 Nov 2025 22:53:32 +0700 Subject: [PATCH] feat/sso-auth: added the use cases --- .../main-use-case/auth-partner/case.go | 276 ++++++++++++++++ .../main-use-case/auth-partner/helper.go | 19 ++ .../main-use-case/auth-partner/lib.go | 150 +++++++++ .../auth-partner/middleware-runner.go | 103 ++++++ .../main-use-case/auth-partner/middleware.go | 9 + .../main-use-case/auth-partner/tycovar.go | 44 +++ .../main-use-case/authentication/case.go | 296 ++++-------------- .../main-use-case/authentication/helper.go | 105 +++++-- .../use-case/main-use-case/user-fes/case.go | 276 ++++++++++++++++ .../use-case/main-use-case/user-fes/helper.go | 18 ++ .../use-case/main-use-case/user-fes/lib.go | 143 +++++++++ .../user-fes/middleware-runner.go | 103 ++++++ .../main-use-case/user-fes/middleware.go | 5 + .../main-use-case/user-fes/tycovar.go | 44 +++ 14 files changed, 1314 insertions(+), 277 deletions(-) create mode 100644 internal/use-case/main-use-case/auth-partner/case.go create mode 100644 internal/use-case/main-use-case/auth-partner/helper.go create mode 100644 internal/use-case/main-use-case/auth-partner/lib.go create mode 100644 internal/use-case/main-use-case/auth-partner/middleware-runner.go create mode 100644 internal/use-case/main-use-case/auth-partner/middleware.go create mode 100644 internal/use-case/main-use-case/auth-partner/tycovar.go create mode 100644 internal/use-case/main-use-case/user-fes/case.go create mode 100644 internal/use-case/main-use-case/user-fes/helper.go create mode 100644 internal/use-case/main-use-case/user-fes/lib.go create mode 100644 internal/use-case/main-use-case/user-fes/middleware-runner.go create mode 100644 internal/use-case/main-use-case/user-fes/middleware.go create mode 100644 internal/use-case/main-use-case/user-fes/tycovar.go diff --git a/internal/use-case/main-use-case/auth-partner/case.go b/internal/use-case/main-use-case/auth-partner/case.go new file mode 100644 index 00000000..f5f7b423 --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/case.go @@ -0,0 +1,276 @@ +package authpartner + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/auth-partner" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" +) + +const source = "device" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.AuthPartner{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.AuthPartner + var dataList []e.AuthPartner + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { + return err + } + + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.AuthPartner + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: &input.Id} + var data *e.AuthPartner + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: &input.Id} + var data *e.AuthPartner + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/auth-partner/helper.go b/internal/use-case/main-use-case/auth-partner/helper.go new file mode 100644 index 00000000..36e1ce7b --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/helper.go @@ -0,0 +1,19 @@ +package authpartner + +import ( + e "simrs-vx/internal/domain/main-entities/auth-partner" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.AuthPartner) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Code = inputSrc.Code + data.Name = inputSrc.Name + data.SecretKey = inputSrc.SecretKey +} diff --git a/internal/use-case/main-use-case/auth-partner/lib.go b/internal/use-case/main-use-case/auth-partner/lib.go new file mode 100644 index 00000000..41253e2c --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/lib.go @@ -0,0 +1,150 @@ +package authpartner + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/auth-partner" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.AuthPartner, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.AuthPartner{} + setData(&input, &data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.AuthPartner, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.AuthPartner{} + pagination := gh.Pagination{} + count := int64(0) + meta := e.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&e.AuthPartner{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.AuthPartner, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.AuthPartner{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if input.Code != nil { + tx = tx.Where("\"Code\" = ?", *input.Code) + } + if input.Id != nil { + tx = tx.Where("\"Id\" = ?", input.Id) + } + + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.AuthPartner, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&input, data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func DeleteData(data *e.AuthPartner, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/auth-partner/middleware-runner.go b/internal/use-case/main-use-case/auth-partner/middleware-runner.go new file mode 100644 index 00000000..d1cabe38 --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/middleware-runner.go @@ -0,0 +1,103 @@ +package authpartner + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/auth-partner" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.AuthPartner) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.AuthPartner) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.AuthPartner) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.AuthPartner) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.AuthPartner) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/main-use-case/auth-partner/middleware.go b/internal/use-case/main-use-case/auth-partner/middleware.go new file mode 100644 index 00000000..4ec1231f --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/middleware.go @@ -0,0 +1,9 @@ +package authpartner + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/main-use-case/auth-partner/tycovar.go b/internal/use-case/main-use-case/auth-partner/tycovar.go new file mode 100644 index 00000000..43f69d98 --- /dev/null +++ b/internal/use-case/main-use-case/auth-partner/tycovar.go @@ -0,0 +1,44 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package authpartner + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/auth-partner" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.AuthPartner, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.AuthPartner, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.AuthPartner, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index d523ae33..94a28fef 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -8,7 +8,6 @@ import ( "time" "github.com/golang-jwt/jwt" - "github.com/google/uuid" a "github.com/karincake/apem" dg "github.com/karincake/apem/db-gorm-pg" ms "github.com/karincake/apem/ms-redis" @@ -19,6 +18,7 @@ import ( pl "simrs-vx/pkg/logger" p "simrs-vx/pkg/password" + eap "simrs-vx/internal/domain/main-entities/auth-partner" eu "simrs-vx/internal/domain/main-entities/user" euf "simrs-vx/internal/domain/main-entities/user-fes" erc "simrs-vx/internal/domain/references/common" @@ -42,13 +42,11 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { // Get User user := &eu.User{Name: input.Name} - // if input.Position_Code != "" { - // user.Position_Code = input.Position_Code - // } - if errCode := getAndCheck(user, user); errCode != "" { + if errCode := getAndCheck(user, user, nil); errCode != "" { return nil, d.FieldErrors{"authentication": d.FieldError{Code: errCode, Message: pl.GenMessage(errCode)}} } + // Check login attempt if user.LoginAttemptCount > 5 { if user.LastSuccessLogin != nil { now := time.Now() @@ -69,6 +67,7 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { } } + // Check password if !p.Check(input.Password, user.Password) { user.LoginAttemptCount++ dg.I.Save(&user) @@ -79,162 +78,14 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-login-unverified", Message: pl.GenMessage("auth-login-unverified")}} } - // Access token prep - id, err := uuid.NewRandom() - if err != nil { - panic(fmt.Sprintf(l.I.Msg("uuid-gen-fail"), err)) - } - if input.Duration == 0 { - input.Duration = 24 * 60 - } - duration := time.Minute * time.Duration(input.Duration) - aUuid := id.String() - atExpires := time.Now().Add(duration).Unix() - atSecretKey := authCfg.AtSecretKey - - // Create Claim + // Data and output population atClaims := jwt.MapClaims{} - atClaims["user_id"] = user.Id - atClaims["user_name"] = user.Name - atClaims["user_contractPosition_code"] = user.ContractPosition_Code - atClaims["uuid"] = aUuid - atClaims["exp"] = atExpires - - // Create output - outputData := d.II{ - "user_id": strconv.Itoa(int(user.Id)), - "user_name": user.Name, - "user_contractPosition_code": user.ContractPosition_Code, - } - - // extra - // role := []string{} - // switch user.ContractPosition_Code { - // case erg.CSCEmp: - // // employee - // employee := ee.Employee{} - // dg.I.Where("\"User_Id\" = ?", user.Id).First(&employee) - // if employee.Id == 0 { - // return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noEmployee", Message: pl.GenMessage("auth-noEmployee")}} - // } - // atClaims["employee_id"] = employee.Id - // outputData["employee_id"] = employee.Id - // role = append(role, "emp-"+string(*employee.Position_Code)) - - // //if employee.Division_Code != nil { - // // atClaims["employee_division_code"] = employee.Division_Code - // // outputData["employee_division_code"] = employee.Division_Code - // //} - - // // employee position - // if employee.Id > 0 && employee.Position_Code != nil { - // atClaims["employee_position_code"] = *employee.Position_Code - // switch *employee.Position_Code { - // case erg.EPCDoc: - // doctor := ed.Doctor{} - // dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&doctor) - // if doctor.Id == 0 { - // return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noDoctor", Message: pl.GenMessage("auth-noDoctor")}} - // } - // atClaims["doctor_code"] = doctor.Code - // outputData["doctor_code"] = doctor.Code - - // // specialist - // if doctor.Specialist_Code != nil { - // atClaims["specialist_code"] = doctor.Specialist_Code - // outputData["specialist_code"] = doctor.Specialist_Code - // } - // if doctor.Subspecialist_Code != nil { - // atClaims["subspecialist_code"] = doctor.Subspecialist_Code - // outputData["subspecialist_code"] = doctor.Subspecialist_Code - // } - // case erg.EPCNur: - // empData := en.Nurse{} - // dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) - // if empData.Id == 0 { - // return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noNurse", Message: pl.GenMessage("auth-noNurse")}} - // } - // atClaims["nurse_code"] = empData.Code - // outputData["nurse_code"] = empData.Code - // case erg.EPCMwi: - // empData := em.Midwife{} - // dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) - // if empData.Id == 0 { - // return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noMidwife", Message: pl.GenMessage("auth-noMidwife")}} - // } - // atClaims["midwife_code"] = empData.Code - // outputData["midwife_code"] = empData.Code - // } - - // errorGetPosition := d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: pl.GenMessage("auth-getData-failed")}} - - // // division position - // divisionPositions, err := getDivisionPosition(employee.Id, &event) - // if err != nil { - // return nil, errorGetPosition - // } - - // // installation position - // installationPositions, err := getInstallationPosition(employee.Id, &event) - // if err != nil { - // return nil, errorGetPosition - // } - - // // unit position - // unitPositions, err := getUnitPosition(employee.Id, &event) - // if err != nil { - // return nil, errorGetPosition - // } - - // // specialist position - // specialistPositions, err := getSpecialistPosition(employee.Id, &event) - // if err != nil { - // return nil, errorGetPosition - // } - - // // subspecialist position - // subspecialistPositions, err := getSubspecialistPosition(employee.Id, &event) - // if err != nil { - // return nil, errorGetPosition - // } - - // role = append(role, divisionPositions...) - // role = append(role, installationPositions...) - // role = append(role, unitPositions...) - // role = append(role, specialistPositions...) - // role = append(role, subspecialistPositions...) - // // atClaims["division_positions"] = divsionPositions - // // outputData["division_positions"] = divsionPositions - // } - // case erg.CSCInt: - // intern := intern.Intern{} - // dg.I.Where("\"User_Id\" = ?", user.Id).First(&intern) - // role = append(role, "int-"+string(*intern.Position_Code)) - // case erg.CSCSys: - // role = append(role, "system") - // } - // atClaims["roles"] = role - // outputData["roles"] = role - if err := populateRoles(user, atClaims, outputData, event); err != nil { + outputData := d.II{} + if err := populateRoles(user, input, atClaims, outputData, event); err != nil { return nil, err } - // Generate jwt - at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) - ats, err := at.SignedString([]byte(atSecretKey)) - if err != nil { - return nil, d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: pl.GenMessage("token-sign-err")}} - } - outputData["accessToken"] = ats - - // Save to redis - now := time.Now() - atx := time.Unix(atExpires, 0) //converting Unix to UTC(to Time object) - err = ms.I.Set(aUuid, strconv.Itoa(int(user.Id)), atx.Sub(now)).Err() - if err != nil { - panic(fmt.Sprintf(l.I.Msg("redis-store-fail"), err.Error())) - } - + // Only manual login tn := time.Now() user.LoginAttemptCount = 0 user.LastSuccessLogin = &tn @@ -252,6 +103,51 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { }, nil } +func GenTokenFes(input euf.LoginDto) (*d.Data, error) { + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Get User Fes + userFes := &euf.UserFes{Name: input.Name, AuthPartner_Code: input.AuthPartner_Code} + if errCode := getAndCheck(userFes, userFes, "AuthPartner"); errCode != "" { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: errCode, Message: pl.GenMessage(errCode)}} + } + // Preload above fails and I am so done, do it manually + authPartner := eap.AuthPartner{} + if err := dg.I.Where("\"Code\" = ?", userFes.AuthPartner_Code).First(&authPartner).Error; err != nil { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-secretKey-invalid", Message: pl.GenMessage("auth-secretKey-invalid")}} + } + if authPartner.SecretKey != input.AuthPartner_SecretKey { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-secretKey-invalid", Message: pl.GenMessage("auth-secretKey-invalid")}} + } + + // Get User + user := &eu.User{Name: userFes.User_Name} + if errCode := getAndCheck(user, user, nil); errCode != "" { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: errCode, Message: pl.GenMessage(errCode)}} + } + + // Data and output population + atClaims := jwt.MapClaims{} + outputData := d.II{} + primInput := eu.LoginDto{Duration: input.Duration} + if err := populateRoles(user, primInput, atClaims, outputData, event); err != nil { + return nil, err + } + + // Current data + return &d.Data{ + Meta: d.IS{ + "source": "authentication", + "structure": "single-data", + "status": "verified", + }, + Data: outputData, + }, nil +} + func RevokeToken(uuid string) { ms.I.Del(uuid) } @@ -327,89 +223,3 @@ func ExtractToken(r *http.Request, tokenType TokenType) (data *pa.AuthInfo, err func GetConfig() { a.ParseCfg(&authCfg) } - -func GenTokenFes(input euf.LoginDto) (*d.Data, error) { - event := pl.Event{ - Feature: "Create", - Source: source, - } - - // Get User Fes - userFes := &euf.UserFes{Name: input.Name, AuthPartner_Code: input.AuthPartner_Code} - if errCode := getAndCheck(userFes, userFes); errCode != "" { - return nil, d.FieldErrors{"authentication": d.FieldError{Code: errCode, Message: pl.GenMessage(errCode)}} - } - if userFes.AuthPartner.SecretKey != input.AuthPartner_SecretKey { - return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-secretKey-invalid", Message: pl.GenMessage("auth-secretKey-invalid")}} - } - - user := &eu.User{Name: userFes.User_Name} - if errCode := getAndCheck(user, user); errCode != "" { - return nil, d.FieldErrors{"authentication": d.FieldError{Code: errCode, Message: pl.GenMessage(errCode)}} - } - - // Access token prep - id, err := uuid.NewRandom() - if err != nil { - panic(fmt.Sprintf(l.I.Msg("uuid-gen-fail"), err)) - } - if input.Duration == 0 { - input.Duration = 24 * 60 - } - duration := time.Minute * time.Duration(input.Duration) - aUuid := id.String() - atExpires := time.Now().Add(duration).Unix() - atSecretKey := authCfg.AtSecretKey - - // Create Claim - atClaims := jwt.MapClaims{} - atClaims["user_id"] = user.Id - atClaims["user_name"] = user.Name - atClaims["user_contractPosition_code"] = user.ContractPosition_Code - atClaims["uuid"] = aUuid - atClaims["exp"] = atExpires - - // Create output - outputData := d.II{ - "user_id": strconv.Itoa(int(user.Id)), - "user_name": user.Name, - "user_contractPosition_code": user.ContractPosition_Code, - } - - // extra - if err := populateRoles(user, atClaims, outputData, event); err != nil { - return nil, err - } - - // Generate jwt - at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) - ats, err := at.SignedString([]byte(atSecretKey)) - if err != nil { - return nil, d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: pl.GenMessage("token-sign-err")}} - } - outputData["accessToken"] = ats - - // Save to redis - now := time.Now() - atx := time.Unix(atExpires, 0) //converting Unix to UTC(to Time object) - err = ms.I.Set(aUuid, strconv.Itoa(int(user.Id)), atx.Sub(now)).Err() - if err != nil { - panic(fmt.Sprintf(l.I.Msg("redis-store-fail"), err.Error())) - } - - tn := time.Now() - user.LoginAttemptCount = 0 - user.LastSuccessLogin = &tn - user.LastAllowdLogin = &tn - dg.I.Save(&user) - - // Current data - return &d.Data{ - Meta: d.IS{ - "source": "authentication", - "structure": "single-data", - "status": "verified", - }, - Data: outputData, - }, nil -} diff --git a/internal/use-case/main-use-case/authentication/helper.go b/internal/use-case/main-use-case/authentication/helper.go index 0da9f943..b7ccc660 100644 --- a/internal/use-case/main-use-case/authentication/helper.go +++ b/internal/use-case/main-use-case/authentication/helper.go @@ -1,10 +1,16 @@ package authentication import ( - dg "github.com/karincake/apem/db-gorm-pg" + "fmt" + "strconv" + "time" "github.com/golang-jwt/jwt" + "github.com/google/uuid" + dg "github.com/karincake/apem/db-gorm-pg" + ms "github.com/karincake/apem/ms-redis" d "github.com/karincake/dodol" + l "github.com/karincake/lepet" pl "simrs-vx/pkg/logger" @@ -29,8 +35,21 @@ import ( ) // just return the error code -func getAndCheck(input, condition any) (eCode string) { - result := dg.I.Where(condition).Find(&input) +func getAndCheck(input, condition any, includes any) (eCode string) { + qry := dg.I.Where(condition) + + // WARNING THIS PRELOAD FAILS + if includes != nil { + if val := includes.(string); val != "" { + qry = qry.Preload(val) + } else if vals := includes.([]string); len(vals) > 0 { + for _, val := range vals { + qry = qry.Preload(val) + } + } + } + + result := qry.First(&input) if result.Error != nil { return "fetch-fail" } else if result.RowsAffected == 0 { @@ -44,18 +63,14 @@ func getDivisionPosition(employee_id uint, event *pl.Event) ([]string, error) { var result []string // get data division_position based on employee_id - data, _, err := udp.ReadListData(edp.ReadListDto{ - FilterDto: edp.FilterDto{Employee_Id: &employee_id}, - Includes: "Division"}, event) + data, _, err := udp.ReadListData(edp.ReadListDto{FilterDto: edp.FilterDto{Employee_Id: &employee_id}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { - if dp.Division != nil { - result = append(result, "div-"+dp.Division.Code+"-"+dp.Code) - } + result = append(result, "div-"+*dp.Division_Code) } } @@ -75,9 +90,7 @@ func getInstallationPosition(employeeId uint, event *pl.Event) ([]string, error) if len(data) > 0 { for _, dp := range data { - if dp.Installation != nil { - result = append(result, "inst-"+dp.Installation.Code+"-"+dp.Code) - } + result = append(result, "inst-"+*dp.Installation_Code+"-"+dp.Code) } } @@ -88,18 +101,14 @@ func getUnitPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data unit_position based on employee_id - data, _, err := uup.ReadListData(eup.ReadListDto{ - FilterDto: eup.FilterDto{Employee_Id: &employeeId}, - Includes: "unit"}, event) + data, _, err := uup.ReadListData(eup.ReadListDto{FilterDto: eup.FilterDto{Employee_Id: &employeeId}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { - if dp.Unit != nil { - result = append(result, "unit-"+dp.Unit.Code+"-"+dp.Code) - } + result = append(result, "unit-"+*dp.Unit_Code+"-"+dp.Code) } } @@ -110,18 +119,14 @@ func getSpecialistPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data unit_position based on employee_id - data, _, err := usp.ReadListData(esp.ReadListDto{ - FilterDto: esp.FilterDto{Employee_Id: &employeeId}, - Includes: "specialist"}, event) + data, _, err := usp.ReadListData(esp.ReadListDto{FilterDto: esp.FilterDto{Employee_Id: &employeeId}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { - if dp.Specialist != nil { - result = append(result, "spec-"+dp.Specialist.Code+"-"+dp.Code) - } + result = append(result, "spec-"+*dp.Specialist_Code+"-"+dp.Code) } } @@ -141,9 +146,7 @@ func getSubspecialistPosition(employeeId uint, event *pl.Event) ([]string, error if len(data) > 0 { for _, dp := range data { - if dp.Subspecialist != nil { - result = append(result, "subspec-"+dp.Subspecialist.Code+"-"+dp.Code) - } + result = append(result, "subspec-"+dp.Subspecialist.Code+"-"+dp.Code) } } @@ -173,7 +176,28 @@ func checkUntPtrClaims(claim map[string]interface{}, key string) *uint { return nil } -func populateRoles(user *eu.User, atClaims jwt.MapClaims, outputData d.II, event pl.Event) error { +func populateRoles(user *eu.User, input eu.LoginDto, atClaims jwt.MapClaims, outputData d.II, event pl.Event) error { + id, err := uuid.NewRandom() + if err != nil { + panic(fmt.Sprintf(l.I.Msg("uuid-gen-fail"), err)) + } + if input.Duration == 0 { + input.Duration = 24 * 60 + } + duration := time.Minute * time.Duration(input.Duration) + aUuid := id.String() + atExpires := time.Now().Add(duration).Unix() + + atClaims["uuid"] = aUuid + atClaims["exp"] = atExpires + atClaims["user_id"] = user.Id + atClaims["user_name"] = user.Name + atClaims["user_contractPosition_code"] = user.ContractPosition_Code + + outputData["user_id"] = user.Id + outputData["user_name"] = user.Name + outputData["user_contractPosition_code"] = user.ContractPosition_Code + roles := []string{} switch user.ContractPosition_Code { case erg.CSCEmp: @@ -187,11 +211,6 @@ func populateRoles(user *eu.User, atClaims jwt.MapClaims, outputData d.II, event outputData["employee_id"] = employee.Id roles = append(roles, "emp-"+string(*employee.Position_Code)) - //if employee.Division_Code != nil { - // atClaims["employee_division_code"] = employee.Division_Code - // outputData["employee_division_code"] = employee.Division_Code - //} - // employee position if employee.Id > 0 && employee.Position_Code != nil { atClaims["employee_position_code"] = *employee.Position_Code @@ -279,8 +298,26 @@ func populateRoles(user *eu.User, atClaims jwt.MapClaims, outputData d.II, event case erg.CSCSys: roles = append(roles, "system") } - atClaims["roles"] = roles outputData["roles"] = roles + + // Generate jwt + atSecretKey := authCfg.AtSecretKey + at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) + ats, err := at.SignedString([]byte(atSecretKey)) + if err != nil { + return d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: pl.GenMessage("token-sign-err")}} + } + outputData["accessToken"] = ats + + // Save to redis + exp, _ := atClaims["exp"].(int64) + now := time.Now() + atx := time.Unix(exp, 0) //converting Unix to UTC(to Time object) + err = ms.I.Set(atClaims["uuid"].(string), strconv.Itoa(int(user.Id)), atx.Sub(now)).Err() + if err != nil { + panic(fmt.Sprintf(l.I.Msg("redis-store-fail"), err.Error())) + } + return nil } diff --git a/internal/use-case/main-use-case/user-fes/case.go b/internal/use-case/main-use-case/user-fes/case.go new file mode 100644 index 00000000..51225927 --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/case.go @@ -0,0 +1,276 @@ +package userfes + +import ( + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + e "simrs-vx/internal/domain/main-entities/user-fes" +) + +const source = "ext-user" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.UserFes{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.UserFes + var dataList []e.UserFes + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { + return err + } + + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.UserFes + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.UserFes + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.UserFes + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/user-fes/helper.go b/internal/use-case/main-use-case/user-fes/helper.go new file mode 100644 index 00000000..b83d303f --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/helper.go @@ -0,0 +1,18 @@ +package userfes + +import ( + e "simrs-vx/internal/domain/main-entities/user-fes" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.UserFes) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + data.Name = inputSrc.Name + data.AuthPartner_Code = inputSrc.AuthPartner_Code + data.User_Name = inputSrc.User_Name +} diff --git a/internal/use-case/main-use-case/user-fes/lib.go b/internal/use-case/main-use-case/user-fes/lib.go new file mode 100644 index 00000000..ce7c8280 --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/lib.go @@ -0,0 +1,143 @@ +package userfes + +import ( + "gorm.io/gorm" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + eu "simrs-vx/internal/domain/main-entities/user-fes" +) + +func CreateData(input eu.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*eu.UserFes, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := eu.UserFes{} + setData(&input, &data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return nil, plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadListData(input eu.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]eu.UserFes, *eu.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []eu.UserFes{} + pagination := gh.Pagination{} + count := int64(0) + meta := eu.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&eu.UserFes{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input eu.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*eu.UserFes, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := eu.UserFes{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data, input.Id).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input eu.UpdateDto, data *eu.UserFes, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&input, data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func DeleteData(data *eu.UserFes, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBDelete") + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Delete(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-delete-fail", + Detail: "Database delete failed", + Raw: err, + } + return pl.SetLogError(event, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/user-fes/middleware-runner.go b/internal/use-case/main-use-case/user-fes/middleware-runner.go new file mode 100644 index 00000000..f669d4cd --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/middleware-runner.go @@ -0,0 +1,103 @@ +package userfes + +import ( + e "simrs-vx/internal/domain/main-entities/user-fes" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.UserFes) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.UserFes) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.UserFes) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.UserFes) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.UserFes) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/main-use-case/user-fes/middleware.go b/internal/use-case/main-use-case/user-fes/middleware.go new file mode 100644 index 00000000..71dac997 --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/middleware.go @@ -0,0 +1,5 @@ +package userfes + +func init() { + +} diff --git a/internal/use-case/main-use-case/user-fes/tycovar.go b/internal/use-case/main-use-case/user-fes/tycovar.go new file mode 100644 index 00000000..008f5cfa --- /dev/null +++ b/internal/use-case/main-use-case/user-fes/tycovar.go @@ -0,0 +1,44 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package userfes + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/user-fes" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.UserFes, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.UserFes, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.UserFes, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw