From 76ca2a2eed98f89f080e70165e755b6c64ca44b1 Mon Sep 17 00:00:00 2001 From: Wiktor Date: Fri, 3 Apr 2026 15:58:35 +0200 Subject: [PATCH 1/3] chore: adapt code to new teleport feature --- app/delivery/web/api/restricted/customer.go | 92 +++++++++---------- app/model/customer.go | 9 ++ app/model/role.go | 2 +- app/repos/customerRepo/customerRepo.go | 2 +- bruno/api_v1/customer/Customer (me).yml | 6 +- .../20260302163123_create_tables_data.sql | 9 +- 6 files changed, 65 insertions(+), 55 deletions(-) diff --git a/app/delivery/web/api/restricted/customer.go b/app/delivery/web/api/restricted/customer.go index 039efcb..da8a7e5 100644 --- a/app/delivery/web/api/restricted/customer.go +++ b/app/delivery/web/api/restricted/customer.go @@ -4,9 +4,9 @@ import ( "strconv" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms" - "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/customerService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" "git.ma-al.com/goc_daniel/b2b/app/utils/response" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" @@ -28,37 +28,34 @@ func CustomerHandlerRoutes(r fiber.Router) fiber.Router { handler := NewCustomerHandler() r.Get("", handler.customerData) - r.Get("/list", handler.listCustomers) + // r.Get("/list", handler.listCustomers) return r } func (h *customerHandler) customerData(fc fiber.Ctx) error { var customerId uint + + user, ok := localeExtractor.GetCustomer(fc) + if !ok || user == nil { + return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) + } + customerIdStr := fc.Query("id") if customerIdStr != "" { - user, ok := fc.Locals("user").(*model.UserSession) - if !ok { - return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) - } id, err := strconv.ParseUint(customerIdStr, 10, 64) if err != nil { return fiber.ErrBadRequest } - if user.UserID != uint(id) && !user.HasPermission(perms.UserReadAny) { + if user.ID != uint(id) && !user.HasPermission(perms.UserReadAny) { return fc.Status(fiber.StatusForbidden). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden))) } customerId = uint(id) } else { - id, ok := fc.Locals("userID").(uint) - if !ok { - return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) - } - customerId = id + customerId = user.ID } customer, err := h.service.GetById(customerId) @@ -70,40 +67,41 @@ func (h *customerHandler) customerData(fc fiber.Ctx) error { return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) } -func (h *customerHandler) listCustomers(fc fiber.Ctx) error { - var customerId uint - customerIdStr := fc.Query("id") - if customerIdStr != "" { - user, ok := fc.Locals("user").(*model.UserSession) - if !ok { - return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) - } - id, err := strconv.ParseUint(customerIdStr, 10, 64) - if err != nil { - return fiber.ErrBadRequest - } +// func (h *customerHandler) listCustomers(fc fiber.Ctx) error { +// var customerId uint +// customerIdStr := fc.Query("id") +// if customerIdStr != "" { - if user.UserID != uint(id) && !user.HasPermission(perms.UserReadAny) { - return fc.Status(fiber.StatusForbidden). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden))) - } +// user, ok := localeExtractor.GetCustomer(fc) +// if !ok || user == nil { +// return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). +// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) +// } +// id, err := strconv.ParseUint(customerIdStr, 10, 64) +// if err != nil { +// return fiber.ErrBadRequest +// } - customerId = uint(id) - } else { - id, ok := fc.Locals("userID").(uint) - if !ok { - return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) - } - customerId = id - } +// if user.UserID != uint(id) && !user.HasPermission(perms.UserReadAny) { +// return fc.Status(fiber.StatusForbidden). +// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden))) +// } - customer, err := h.service.GetById(customerId) - if err != nil { - return fc.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) - } +// customerId = uint(id) +// } else { +// id, ok := fc.Locals("userID").(uint) +// if !ok { +// return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). +// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) +// } +// customerId = id +// } - return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) -} +// customer, err := h.service.GetById(customerId) +// if err != nil { +// return fc.Status(responseErrors.GetErrorStatus(err)). +// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) +// } + +// return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) +// } diff --git a/app/model/customer.go b/app/model/customer.go index d036e5b..ccf2fe5 100644 --- a/app/model/customer.go +++ b/app/model/customer.go @@ -34,6 +34,15 @@ type Customer struct { DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` } +func (u *Customer) HasPermission(permission perms.Permission) bool { + for _, p := range u.Role.Permissions { + if p.Name == permission { + return true + } + } + return false +} + // AuthProvider represents the authentication provider type AuthProvider string diff --git a/app/model/role.go b/app/model/role.go index 3c663b5..2ea0789 100644 --- a/app/model/role.go +++ b/app/model/role.go @@ -3,7 +3,7 @@ package model type Role struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"size:64" json:"name"` - Permissions []Permission `gorm:"many2many:b2b_role_permissions;" json:"-"` + Permissions []Permission `gorm:"many2many:b2b_role_permissions;" json:"permissions"` } func (Role) TableName() string { diff --git a/app/repos/customerRepo/customerRepo.go b/app/repos/customerRepo/customerRepo.go index 058d5fd..b46890f 100644 --- a/app/repos/customerRepo/customerRepo.go +++ b/app/repos/customerRepo/customerRepo.go @@ -19,7 +19,7 @@ func (repo *CustomerRepo) Get(id uint) (*model.Customer, error) { var customer model.Customer err := db.DB. - Preload("Role"). + Preload("Role.Permissions"). First(&customer, id). Error diff --git a/bruno/api_v1/customer/Customer (me).yml b/bruno/api_v1/customer/Customer (me).yml index 253bead..891919e 100644 --- a/bruno/api_v1/customer/Customer (me).yml +++ b/bruno/api_v1/customer/Customer (me).yml @@ -5,11 +5,7 @@ info: http: method: GET - url: "{{bas_url}}/restricted/customer?id=1" - params: - - name: id - value: "1" - type: query + url: "{{bas_url}}/restricted/customer" auth: inherit settings: diff --git a/i18n/migrations/20260302163123_create_tables_data.sql b/i18n/migrations/20260302163123_create_tables_data.sql index ce62f1b..dafebf7 100644 --- a/i18n/migrations/20260302163123_create_tables_data.sql +++ b/i18n/migrations/20260302163123_create_tables_data.sql @@ -35,5 +35,12 @@ INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('2', 'user.write.any'); INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('3', 'user.delete.any'); INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('4', 'currency.write'); - +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '1'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '2'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '3'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '4'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '1'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '2'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '3'); +INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '4'); -- +goose Down \ No newline at end of file From 813d1f48791d30f2a0e61f78dee043fdae9146f8 Mon Sep 17 00:00:00 2001 From: Wiktor Date: Tue, 7 Apr 2026 09:28:39 +0200 Subject: [PATCH 2/3] feat: add customer list, modify pagination utils --- app/delivery/web/api/restricted/customer.go | 61 ++++++++----------- app/model/customer.go | 2 +- app/repos/customerRepo/customerRepo.go | 12 ++++ app/service/authService/auth.go | 1 - app/service/authService/google_oauth.go | 1 - .../customerService/customerService.go | 6 ++ app/utils/query/find/find.go | 18 +----- bruno/api_v1/customer/Customer list.yml | 15 +++++ 8 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 bruno/api_v1/customer/Customer list.yml diff --git a/app/delivery/web/api/restricted/customer.go b/app/delivery/web/api/restricted/customer.go index da8a7e5..6b3ea60 100644 --- a/app/delivery/web/api/restricted/customer.go +++ b/app/delivery/web/api/restricted/customer.go @@ -4,10 +4,12 @@ import ( "strconv" "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms" + "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/customerService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params" "git.ma-al.com/goc_daniel/b2b/app/utils/response" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "github.com/gofiber/fiber/v3" @@ -28,7 +30,7 @@ func CustomerHandlerRoutes(r fiber.Router) fiber.Router { handler := NewCustomerHandler() r.Get("", handler.customerData) - // r.Get("/list", handler.listCustomers) + r.Get("/list", handler.listCustomers) return r } @@ -67,41 +69,28 @@ func (h *customerHandler) customerData(fc fiber.Ctx) error { return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) } -// func (h *customerHandler) listCustomers(fc fiber.Ctx) error { -// var customerId uint -// customerIdStr := fc.Query("id") -// if customerIdStr != "" { +func (h *customerHandler) listCustomers(fc fiber.Ctx) error { + p, filt, err := query_params.ParseFilters[model.Customer](fc, columnMappingListProducts) + if err != nil { + return fc.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) + } -// user, ok := localeExtractor.GetCustomer(fc) -// if !ok || user == nil { -// return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). -// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) -// } -// id, err := strconv.ParseUint(customerIdStr, 10, 64) -// if err != nil { -// return fiber.ErrBadRequest -// } + user, ok := localeExtractor.GetCustomer(fc) + if !ok || user == nil { + return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) + } + if !user.HasPermission(perms.UserReadAny) { + return fc.Status(fiber.StatusForbidden). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden))) + } -// if user.UserID != uint(id) && !user.HasPermission(perms.UserReadAny) { -// return fc.Status(fiber.StatusForbidden). -// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden))) -// } + customer, err := h.service.Find(user.LangID, p, filt) + if err != nil { + return fc.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) + } -// customerId = uint(id) -// } else { -// id, ok := fc.Locals("userID").(uint) -// if !ok { -// return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). -// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute))) -// } -// customerId = id -// } - -// customer, err := h.service.GetById(customerId) -// if err != nil { -// return fc.Status(responseErrors.GetErrorStatus(err)). -// JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) -// } - -// return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) -// } + return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) +} diff --git a/app/model/customer.go b/app/model/customer.go index ccf2fe5..9421862 100644 --- a/app/model/customer.go +++ b/app/model/customer.go @@ -15,7 +15,7 @@ type Customer struct { FirstName string `gorm:"size:100" json:"first_name"` LastName string `gorm:"size:100" json:"last_name"` RoleID uint `gorm:"column:role_id;not null;default:1" json:"-"` - Role Role `gorm:"foreignKey:RoleID" json:"role"` + Role *Role `gorm:"foreignKey:RoleID" json:"role,omitempty"` Provider AuthProvider `gorm:"type:varchar(20);default:'local'" json:"provider"` ProviderID string `gorm:"size:255" json:"provider_id,omitempty"` // ID from OAuth provider AvatarURL string `gorm:"size:500" json:"avatar_url,omitempty"` diff --git a/app/repos/customerRepo/customerRepo.go b/app/repos/customerRepo/customerRepo.go index b46890f..7a979bb 100644 --- a/app/repos/customerRepo/customerRepo.go +++ b/app/repos/customerRepo/customerRepo.go @@ -3,10 +3,13 @@ package customerRepo import ( "git.ma-al.com/goc_daniel/b2b/app/db" "git.ma-al.com/goc_daniel/b2b/app/model" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" ) type UICustomerRepo interface { Get(id uint) (*model.Customer, error) + Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) } type CustomerRepo struct{} @@ -25,3 +28,12 @@ func (repo *CustomerRepo) Get(id uint) (*model.Customer, error) { return &customer, err } + +func (repo *CustomerRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) { + found, err := find.Paginate[model.Customer](langId, p, db.DB. + Model(&model.Customer{}). + Scopes(filt.All()...), + ) + + return &found, err +} diff --git a/app/service/authService/auth.go b/app/service/authService/auth.go index dc4a35b..ba1fa67 100644 --- a/app/service/authService/auth.go +++ b/app/service/authService/auth.go @@ -153,7 +153,6 @@ func (s *AuthService) Register(req *model.RegisterRequest) error { Password: string(hashedPassword), FirstName: req.FirstName, LastName: req.LastName, - Role: model.Role{}, Provider: model.ProviderLocal, IsActive: false, EmailVerified: false, diff --git a/app/service/authService/google_oauth.go b/app/service/authService/google_oauth.go index c26da16..d8c1820 100644 --- a/app/service/authService/google_oauth.go +++ b/app/service/authService/google_oauth.go @@ -150,7 +150,6 @@ func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model. Provider: model.ProviderGoogle, ProviderID: info.ID, AvatarURL: info.Picture, - Role: model.Role{}, IsActive: true, EmailVerified: true, LangID: 2, // default is english diff --git a/app/service/customerService/customerService.go b/app/service/customerService/customerService.go index 7af553c..dbaeb24 100644 --- a/app/service/customerService/customerService.go +++ b/app/service/customerService/customerService.go @@ -3,6 +3,8 @@ package customerService import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/repos/customerRepo" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" ) type CustomerService struct { @@ -18,3 +20,7 @@ func New() *CustomerService { func (s *CustomerService) GetById(id uint) (*model.Customer, error) { return s.repo.Get(id) } + +func (s *CustomerService) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) { + return s.repo.Find(langId, p, filt) +} diff --git a/app/utils/query/find/find.go b/app/utils/query/find/find.go index 7d810ec..57ef813 100644 --- a/app/utils/query/find/find.go +++ b/app/utils/query/find/find.go @@ -1,7 +1,6 @@ package find import ( - "errors" "reflect" "strings" @@ -28,18 +27,13 @@ type Found[T any] struct { Spec map[string]interface{} `json:"spec,omitempty"` } -// Wraps given query adding limit, offset clauses and SQL_CALC_FOUND_ROWS to it -// and running SELECT FOUND_ROWS() afterwards to fetch the total number -// (ignoring LIMIT) of results. The final results are wrapped into the -// [find.Found] type. func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error) { var items []T - var count uint64 + var count int64 - // stmt.Debug() + stmt.Count(&count) err := stmt. - Clauses(SqlCalcFound()). Offset(paging.Offset()). Limit(paging.Limit()). Find(&items). @@ -48,14 +42,6 @@ func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error return Found[T]{}, err } - countInterface, ok := stmt.Get(FOUND_ROWS_CTX_KEY) - if !ok { - return Found[T]{}, errors.New(FOUND_ROWS_CTX_KEY + " value was not found in the gorm db context") - } - if count, ok = countInterface.(uint64); !ok { - return Found[T]{}, errors.New("failed to cast value under " + FOUND_ROWS_CTX_KEY + " to uint64") - } - columnsSpec := GetColumnsSpec[T](langID) return Found[T]{ diff --git a/bruno/api_v1/customer/Customer list.yml b/bruno/api_v1/customer/Customer list.yml new file mode 100644 index 0000000..0d5bc26 --- /dev/null +++ b/bruno/api_v1/customer/Customer list.yml @@ -0,0 +1,15 @@ +info: + name: Customer list + type: http + seq: 3 + +http: + method: GET + url: "{{bas_url}}/restricted/customer/list" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 From 918729736785f544c847eed2a8a535fb4c72262d Mon Sep 17 00:00:00 2001 From: Wiktor Date: Tue, 7 Apr 2026 10:32:30 +0200 Subject: [PATCH 3/3] refactor: move lists to their representative repos --- app/delivery/web/api/restricted/customer.go | 9 +- app/delivery/web/api/restricted/list.go | 99 -------------- app/delivery/web/api/restricted/product.go | 34 +++++ app/delivery/web/init.go | 4 - app/model/customer.go | 1 - app/repos/customerRepo/customerRepo.go | 54 +++++++- app/repos/listRepo/listRepo.go | 121 ------------------ app/repos/productsRepo/productsRepo.go | 66 ++++++++++ .../customerService/customerService.go | 2 +- app/service/listService/listService.go | 26 ---- app/service/productService/productService.go | 7 + bruno/api_v1/product/Products List.yml | 5 +- repository/currencyRepo/currencyRepo.go | 53 -------- 13 files changed, 167 insertions(+), 314 deletions(-) delete mode 100644 app/delivery/web/api/restricted/list.go delete mode 100644 app/repos/listRepo/listRepo.go delete mode 100644 app/service/listService/listService.go delete mode 100644 repository/currencyRepo/currencyRepo.go diff --git a/app/delivery/web/api/restricted/customer.go b/app/delivery/web/api/restricted/customer.go index 6b3ea60..6f953b3 100644 --- a/app/delivery/web/api/restricted/customer.go +++ b/app/delivery/web/api/restricted/customer.go @@ -70,7 +70,7 @@ func (h *customerHandler) customerData(fc fiber.Ctx) error { } func (h *customerHandler) listCustomers(fc fiber.Ctx) error { - p, filt, err := query_params.ParseFilters[model.Customer](fc, columnMappingListProducts) + p, filt, err := query_params.ParseFilters[model.Customer](fc, columnMappingListUsers) if err != nil { return fc.Status(responseErrors.GetErrorStatus(err)). JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err))) @@ -94,3 +94,10 @@ func (h *customerHandler) listCustomers(fc fiber.Ctx) error { return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK))) } + +var columnMappingListUsers map[string]string = map[string]string{ + "user_id": "users.id", + "email": "users.email", + "first_name": "users.first_name", + "second_name": "users.second_name", +} diff --git a/app/delivery/web/api/restricted/list.go b/app/delivery/web/api/restricted/list.go deleted file mode 100644 index c6b3116..0000000 --- a/app/delivery/web/api/restricted/list.go +++ /dev/null @@ -1,99 +0,0 @@ -package restricted - -import ( - "git.ma-al.com/goc_daniel/b2b/app/config" - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/service/listService" - "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" - "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" - "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params" - "git.ma-al.com/goc_daniel/b2b/app/utils/response" - "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" - "github.com/gofiber/fiber/v3" -) - -// ListHandler handles endpoints that list various things (e.g. products or users) -type ListHandler struct { - listService *listService.ListService - config *config.Config -} - -// NewListHandler creates a new ListHandler instance -func NewListHandler() *ListHandler { - listService := listService.New() - return &ListHandler{ - listService: listService, - config: config.Get(), - } -} - -func ListHandlerRoutes(r fiber.Router) fiber.Router { - handler := NewListHandler() - - r.Get("/list-products", handler.ListProducts) - r.Get("/list-users", handler.ListUsers) - - return r -} - -func (h *ListHandler) ListProducts(c fiber.Ctx) error { - paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingListProducts) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - id_lang, ok := localeExtractor.GetLangID(c) - if !ok { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - - list, err := h.listService.ListProducts(id_lang, paging, filters) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - return c.JSON(response.Make(&list.Items, int(list.Count), i18n.T_(c, response.Message_OK))) -} - -var columnMappingListProducts map[string]string = map[string]string{ - "product_id": "ps.id_product", - "name": "pl.name", - "reference": "p.reference", - "category_name": "cl.name", - "category_id": "cp.id_category", - "quantity": "sa.quantity", -} - -func (h *ListHandler) ListUsers(c fiber.Ctx) error { - paging, filters, err := query_params.ParseFilters[model.Customer](c, columnMappingListUsers) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - id_lang, ok := localeExtractor.GetLangID(c) - if !ok { - return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) - } - - list, err := h.listService.ListUsers(id_lang, paging, filters) - if err != nil { - return c.Status(responseErrors.GetErrorStatus(err)). - JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - - return c.JSON(response.Make(&list.Items, int(list.Count), i18n.T_(c, response.Message_OK))) -} - -var columnMappingListUsers map[string]string = map[string]string{ - "user_id": "users.id", - "email": "users.email", - "first_name": "users.first_name", - "second_name": "users.second_name", - "role": "users.role", -} diff --git a/app/delivery/web/api/restricted/product.go b/app/delivery/web/api/restricted/product.go index d4fa8ce..ddd8677 100644 --- a/app/delivery/web/api/restricted/product.go +++ b/app/delivery/web/api/restricted/product.go @@ -4,10 +4,12 @@ import ( "strconv" "git.ma-al.com/goc_daniel/b2b/app/config" + "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/productService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params" "git.ma-al.com/goc_daniel/b2b/app/utils/response" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" "github.com/gofiber/fiber/v3" @@ -31,6 +33,7 @@ func ProductsHandlerRoutes(r fiber.Router) fiber.Router { handler := NewProductsHandler() r.Get("/:id/:country_id/:quantity", handler.GetProductJson) + r.Get("/list", handler.ListProducts) return r } @@ -73,3 +76,34 @@ func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error { return c.JSON(response.Make(&productJson, 1, i18n.T_(c, response.Message_OK))) } + +func (h *ProductsHandler) ListProducts(c fiber.Ctx) error { + paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingListProducts) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + id_lang, ok := localeExtractor.GetLangID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + list, err := h.productService.Find(id_lang, paging, filters) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&list.Items, int(list.Count), i18n.T_(c, response.Message_OK))) +} + +var columnMappingListProducts map[string]string = map[string]string{ + "product_id": "ps.id_product", + "name": "pl.name", + "reference": "p.reference", + "category_name": "cl.name", + "category_id": "cp.id_category", + "quantity": "sa.quantity", +} diff --git a/app/delivery/web/init.go b/app/delivery/web/init.go index ad75e75..9d673f5 100644 --- a/app/delivery/web/init.go +++ b/app/delivery/web/init.go @@ -97,10 +97,6 @@ func (s *Server) Setup() error { productTranslation := s.restricted.Group("/product-translation") restricted.ProductTranslationHandlerRoutes(productTranslation) - // lists of things routes (restricted) - list := s.restricted.Group("/list") - restricted.ListHandlerRoutes(list) - product := s.restricted.Group("/product") restricted.ProductsHandlerRoutes(product) diff --git a/app/model/customer.go b/app/model/customer.go index 9421862..77102ad 100644 --- a/app/model/customer.go +++ b/app/model/customer.go @@ -177,5 +177,4 @@ type UserInList struct { Email string `gorm:"column:email" json:"email"` FirstName string `gorm:"column:first_name" json:"first_name"` LastName string `gorm:"column:last_name" json:"last_name"` - Role string `gorm:"column:role" json:"role"` } diff --git a/app/repos/customerRepo/customerRepo.go b/app/repos/customerRepo/customerRepo.go index 7a979bb..9f325c2 100644 --- a/app/repos/customerRepo/customerRepo.go +++ b/app/repos/customerRepo/customerRepo.go @@ -9,7 +9,7 @@ import ( type UICustomerRepo interface { Get(id uint) (*model.Customer, error) - Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) + Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.UserInList], error) } type CustomerRepo struct{} @@ -29,11 +29,57 @@ func (repo *CustomerRepo) Get(id uint) (*model.Customer, error) { return &customer, err } -func (repo *CustomerRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) { - found, err := find.Paginate[model.Customer](langId, p, db.DB. - Model(&model.Customer{}). +func (repo *CustomerRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.UserInList], error) { + found, err := find.Paginate[model.UserInList](langId, p, db.DB. + Table("b2b_customers AS users"). + Select(` + users.id AS id, + users.email AS email, + users.first_name AS first_name, + users.last_name AS last_name + `). Scopes(filt.All()...), ) return &found, err } + +// func (repo *ListRepo) ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) { +// var list []model.UserInList +// var total int64 + +// query := db.Get(). +// Table("b2b_customers AS users"). +// Select(` +// users.id AS id, +// users.email AS email, +// users.first_name AS first_name, +// users.last_name AS last_name, +// users.role AS role +// `) + +// // Apply all filters +// if filt != nil { +// filt.ApplyAll(query) +// } + +// // run counter first as query is without limit and offset +// err := query.Count(&total).Error +// if err != nil { +// return find.Found[model.UserInList]{}, err +// } + +// err = query. +// Order("users.id DESC"). +// Limit(p.Limit()). +// Offset(p.Offset()). +// Find(&list).Error +// if err != nil { +// return find.Found[model.UserInList]{}, err +// } + +// return find.Found[model.UserInList]{ +// Items: list, +// Count: uint(total), +// }, nil +// } diff --git a/app/repos/listRepo/listRepo.go b/app/repos/listRepo/listRepo.go deleted file mode 100644 index d31ebda..0000000 --- a/app/repos/listRepo/listRepo.go +++ /dev/null @@ -1,121 +0,0 @@ -package listRepo - -import ( - "git.ma-al.com/goc_daniel/b2b/app/config" - "git.ma-al.com/goc_daniel/b2b/app/db" - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" - "git.ma-al.com/goc_marek/gormcol" - "github.com/WinterYukky/gorm-extra-clause-plugin/exclause" -) - -type UIListRepo interface { - ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) - ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) -} - -type ListRepo struct{} - -func New() UIListRepo { - return &ListRepo{} -} - -func (repo *ListRepo) ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { - var list []model.ProductInList - var total int64 - - query := db.Get(). - Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps"). - Select(` - ps.id_product AS product_id, - pl.name AS name, - pl.link_rewrite AS link_rewrite, - CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link, - cl.name AS category_name, - p.reference AS reference, - COALESCE(v.variants_number, 0) AS variants_number, - sa.quantity AS quantity - `, config.Get().Image.ImagePrefix). - Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product"). - Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang). - Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1"). - Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang). - Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product"). - Joins("LEFT JOIN variants v ON v.id_product = ps.id_product"). - Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0"). - Where("ps.active = ?", 1). - Group("ps.id_product"). - Clauses(exclause.With{CTEs: []exclause.CTE{ - { - Name: "variants", - Subquery: exclause.Subquery{DB: db.Get().Model(&dbmodel.PsProductAttributeShop{}).Select("id_product", "COUNT(*) AS variants_number").Group("id_product")}, - }, - }}) - - // Apply all filters - if filt != nil { - filt.ApplyAll(query) - } - - // run counter first as query is without limit and offset - err := query.Count(&total).Error - if err != nil { - return find.Found[model.ProductInList]{}, err - } - - err = query. - Order("ps.id_product DESC"). - Limit(p.Limit()). - Offset(p.Offset()). - Find(&list).Error - if err != nil { - return find.Found[model.ProductInList]{}, err - } - - return find.Found[model.ProductInList]{ - Items: list, - Count: uint(total), - }, nil -} - -func (repo *ListRepo) ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) { - var list []model.UserInList - var total int64 - - query := db.Get(). - Table("b2b_customers AS users"). - Select(` - users.id AS id, - users.email AS email, - users.first_name AS first_name, - users.last_name AS last_name, - users.role AS role - `) - - // Apply all filters - if filt != nil { - filt.ApplyAll(query) - } - - // run counter first as query is without limit and offset - err := query.Count(&total).Error - if err != nil { - return find.Found[model.UserInList]{}, err - } - - err = query. - Order("users.id DESC"). - Limit(p.Limit()). - Offset(p.Offset()). - Find(&list).Error - if err != nil { - return find.Found[model.UserInList]{}, err - } - - return find.Found[model.UserInList]{ - Items: list, - Count: uint(total), - }, nil -} diff --git a/app/repos/productsRepo/productsRepo.go b/app/repos/productsRepo/productsRepo.go index 7c6c08f..341b348 100644 --- a/app/repos/productsRepo/productsRepo.go +++ b/app/repos/productsRepo/productsRepo.go @@ -4,11 +4,19 @@ import ( "encoding/json" "fmt" + "git.ma-al.com/goc_daniel/b2b/app/config" "git.ma-al.com/goc_daniel/b2b/app/db" + "git.ma-al.com/goc_daniel/b2b/app/model" + "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" + "git.ma-al.com/goc_marek/gormcol" + "github.com/WinterYukky/gorm-extra-clause-plugin/exclause" ) type UIProductsRepo interface { GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) + Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) } type ProductsRepo struct{} @@ -37,3 +45,61 @@ func (repo *ProductsRepo) GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_custo raw := json.RawMessage(productStr) return &raw, nil } + +func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) { + var list []model.ProductInList + var total int64 + + query := db.Get(). + Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps"). + Select(` + ps.id_product AS product_id, + pl.name AS name, + pl.link_rewrite AS link_rewrite, + CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link, + cl.name AS category_name, + p.reference AS reference, + COALESCE(v.variants_number, 0) AS variants_number, + sa.quantity AS quantity + `, config.Get().Image.ImagePrefix). + Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product"). + Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang). + Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1"). + Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang). + Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product"). + Joins("LEFT JOIN variants v ON v.id_product = ps.id_product"). + Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0"). + Where("ps.active = ?", 1). + Group("ps.id_product"). + Clauses(exclause.With{CTEs: []exclause.CTE{ + { + Name: "variants", + Subquery: exclause.Subquery{DB: db.Get().Model(&dbmodel.PsProductAttributeShop{}).Select("id_product", "COUNT(*) AS variants_number").Group("id_product")}, + }, + }}). + Order("ps.id_product DESC") + + // Apply all filters + if filt != nil { + filt.ApplyAll(query) + } + + // run counter first as query is without limit and offset + err := query.Count(&total).Error + if err != nil { + return find.Found[model.ProductInList]{}, err + } + + err = query. + Limit(p.Limit()). + Offset(p.Offset()). + Find(&list).Error + if err != nil { + return find.Found[model.ProductInList]{}, err + } + + return find.Found[model.ProductInList]{ + Items: list, + Count: uint(total), + }, nil +} diff --git a/app/service/customerService/customerService.go b/app/service/customerService/customerService.go index dbaeb24..f9f2f4a 100644 --- a/app/service/customerService/customerService.go +++ b/app/service/customerService/customerService.go @@ -21,6 +21,6 @@ func (s *CustomerService) GetById(id uint) (*model.Customer, error) { return s.repo.Get(id) } -func (s *CustomerService) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Customer], error) { +func (s *CustomerService) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.UserInList], error) { return s.repo.Find(langId, p, filt) } diff --git a/app/service/listService/listService.go b/app/service/listService/listService.go deleted file mode 100644 index d3d168b..0000000 --- a/app/service/listService/listService.go +++ /dev/null @@ -1,26 +0,0 @@ -package listService - -import ( - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/repos/listRepo" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" -) - -type ListService struct { - listRepo listRepo.UIListRepo -} - -func New() *ListService { - return &ListService{ - listRepo: listRepo.New(), - } -} - -func (s *ListService) ListProducts(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { - return s.listRepo.ListProducts(id_lang, p, filters) -} - -func (s *ListService) ListUsers(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.UserInList], error) { - return s.listRepo.ListUsers(id_lang, p, filters) -} diff --git a/app/service/productService/productService.go b/app/service/productService/productService.go index 66245f1..1a1620e 100644 --- a/app/service/productService/productService.go +++ b/app/service/productService/productService.go @@ -3,8 +3,11 @@ package productService import ( "encoding/json" + "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" ) type ProductService struct { @@ -25,3 +28,7 @@ func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_ return products, nil } + +func (s *ProductService) Find(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) { + return s.productsRepo.Find(id_lang, p, filters) +} diff --git a/bruno/api_v1/product/Products List.yml b/bruno/api_v1/product/Products List.yml index cc07f08..6763495 100644 --- a/bruno/api_v1/product/Products List.yml +++ b/bruno/api_v1/product/Products List.yml @@ -5,7 +5,7 @@ info: http: method: GET - url: "{{bas_url}}/restricted/list/list-products?p=1&elems=30&sort=product_id,asc&category_id_in=243&reference=~62" + url: "{{bas_url}}/restricted/product/list?p=1&elems=30&sort=product_id,asc&category_id_in=243&reference=~62" params: - name: p value: "1" @@ -25,9 +25,6 @@ http: body: type: json data: "" - auth: - type: bearer - token: "{{token}}" settings: encodeUrl: true diff --git a/repository/currencyRepo/currencyRepo.go b/repository/currencyRepo/currencyRepo.go deleted file mode 100644 index 97b1b5e..0000000 --- a/repository/currencyRepo/currencyRepo.go +++ /dev/null @@ -1,53 +0,0 @@ -package currencyRepo - -import ( - "git.ma-al.com/goc_daniel/b2b/app/db" - "git.ma-al.com/goc_daniel/b2b/app/model" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" - "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" -) - -type UICurrencyRepo interface { - CreateConversionRate(currencyRate *model.CurrencyRate) error - Get(id uint) (*model.Currency, error) -} - -type CurrencyRepo struct{} - -func New() UICurrencyRepo { - return &CurrencyRepo{} -} - -func (repo *CurrencyRepo) CreateConversionRate(currencyRate *model.CurrencyRate) error { - return db.DB.Debug().Create(currencyRate).Error -} - -func (repo *CurrencyRepo) Get(id uint) (*model.Currency, error) { - var currency model.Currency - - err := db.DB.Table("b2b_currencies c"). - Select("c.*, r.conversion_rate"). - Joins(` - LEFT JOIN b2b_currency_rates r - ON r.b2b_id_currency = c.id - AND r.created_at = ( - SELECT MAX(created_at) - FROM b2b_currency_rates - WHERE b2b_id_currency = c.id - ) - `). - Where("c.id = ?", id). - Scan(¤cy).Error - - return ¤cy, err -} - -func (repo *CurrencyRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Currency], error) { - - found, err := find.Paginate[model.Currency](langId, p, db.DB. - Model(&model.Currency{}). - Scopes(filt.All()...), - ) - - return &found, err -}