endpoint returning tree of categories
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ bin/
|
|||||||
i18n/*.json
|
i18n/*.json
|
||||||
*_templ.go
|
*_templ.go
|
||||||
tmp/main
|
tmp/main
|
||||||
|
test.go
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package restricted
|
package restricted
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||||
@@ -29,7 +31,21 @@ func MenuHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
|
func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
|
||||||
menu, err := h.menuService.GetMenu()
|
|
||||||
|
id_shop_attribute := c.Query("shopID")
|
||||||
|
id_shop, err := strconv.Atoi(id_shop_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
menu, err := h.menuService.GetMenu(uint(id_shop), uint(id_lang))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
|||||||
@@ -81,4 +81,19 @@ type ProductFilters struct {
|
|||||||
InStock uint `query:"stock,omitempty"`
|
InStock uint `query:"stock,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ScannedCategory struct {
|
||||||
|
CategoryID uint `gorm:"column:ID;primaryKey"`
|
||||||
|
Name string `gorm:"column:name"`
|
||||||
|
Active uint `gorm:"column:active"`
|
||||||
|
Position uint `gorm:"column:position"`
|
||||||
|
ParentID uint `gorm:"column:id_parent"`
|
||||||
|
IsRoot uint `gorm:"column:is_root_category"`
|
||||||
|
}
|
||||||
|
type Category struct {
|
||||||
|
CategoryID uint `json:"category_id" form:"category_id"`
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
Active uint `json:"active" form:"active"`
|
||||||
|
Subcategories []Category `json:"subcategories" form:"subcategories"`
|
||||||
|
}
|
||||||
|
|
||||||
type FeatVal = map[uint][]uint
|
type FeatVal = map[uint][]uint
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
package menuService
|
package menuService
|
||||||
|
|
||||||
import "git.ma-al.com/goc_daniel/b2b/repository/categoriesRepo"
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/repository/categoriesRepo"
|
||||||
|
)
|
||||||
|
|
||||||
type MenuService struct {
|
type MenuService struct {
|
||||||
categoriesRepo categoriesRepo.UICategoriesRepo
|
categoriesRepo categoriesRepo.UICategoriesRepo
|
||||||
@@ -12,35 +18,73 @@ func New() *MenuService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MenuService) GetMenu() (string, error) {
|
func (s *MenuService) GetMenu(id_shop uint, id_lang uint) (model.Category, error) {
|
||||||
// var menu_json string
|
all_categories, err := s.categoriesRepo.GetAllCategories(id_shop, id_lang)
|
||||||
|
if err != nil {
|
||||||
|
return model.Category{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// products, err := s.listProductsRepo.GetListing(id_shop, id_lang, p, filters)
|
// find the root
|
||||||
// if err != nil {
|
root_index := 0
|
||||||
// return products, err
|
root_found := false
|
||||||
// }
|
for i := 0; i < len(all_categories); i++ {
|
||||||
|
if all_categories[i].IsRoot == 1 {
|
||||||
|
root_index = i
|
||||||
|
root_found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !root_found {
|
||||||
|
return model.Category{}, responseErrors.ErrNoRootFound
|
||||||
|
}
|
||||||
|
|
||||||
// var loopErr error
|
// now create the children and reorder them according to position
|
||||||
// parallel.ForEach(products.Items, func(t model.Product, i int) {
|
id_to_index := make(map[uint]int)
|
||||||
// // products.Items[i].PriceTaxed *= currRate.Rate.InexactFloat64()
|
for i := 0; i < len(all_categories); i++ {
|
||||||
// // products.Items[i].PriceTaxed = tiny_util.RoundUpMonetary(products.Items[i].PriceTaxed)
|
id_to_index[all_categories[i].CategoryID] = i
|
||||||
|
}
|
||||||
|
|
||||||
// if products.Items[i].Name.IsNull() {
|
children_indices := make(map[int][]ChildWithPosition)
|
||||||
// translation, err := s.listProductsRepo.GetTranslation(ctx, products.Items[i].ID, defaults.DefaultLanguageID)
|
for i := 0; i < len(all_categories); i++ {
|
||||||
// if err != nil {
|
parent_index := id_to_index[all_categories[i].ParentID]
|
||||||
// loopErr = err
|
children_indices[parent_index] = append(children_indices[parent_index], ChildWithPosition{Index: i, Position: all_categories[i].Position})
|
||||||
// return
|
}
|
||||||
// }
|
|
||||||
// products.Items[i].Name = nullable.FromPrimitiveString(translation.Name)
|
|
||||||
// products.Items[i].DescriptionShort = nullable.FromPrimitiveString(translation.DescriptionShort)
|
|
||||||
// products.Items[i].LinkRewrite = nullable.FromPrimitiveString(translation.LinkRewrite)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// if loopErr != nil {
|
|
||||||
// return products, errs.Handled(span, loopErr, errs.InternalError, errs.ERR_TODO)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return products, nil
|
for key := range children_indices {
|
||||||
|
sort.Sort(ByPosition(children_indices[key]))
|
||||||
|
}
|
||||||
|
|
||||||
return "", nil
|
// finally, create the tree
|
||||||
|
tree := s.createTree(root_index, &all_categories, &children_indices)
|
||||||
|
|
||||||
|
return tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category {
|
||||||
|
node := s.scannedToNormalCategory((*all_categories)[index])
|
||||||
|
|
||||||
|
for i := 0; i < len((*children_indices)[index]); i++ {
|
||||||
|
node.Subcategories = append(node.Subcategories, s.createTree((*children_indices)[index][i].Index, all_categories, children_indices))
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
||||||
|
var normal model.Category
|
||||||
|
normal.Active = scanned.Active
|
||||||
|
normal.CategoryID = scanned.CategoryID
|
||||||
|
normal.Name = scanned.Name
|
||||||
|
normal.Subcategories = []model.Category{}
|
||||||
|
return normal
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChildWithPosition struct {
|
||||||
|
Index int
|
||||||
|
Position uint
|
||||||
|
}
|
||||||
|
type ByPosition []ChildWithPosition
|
||||||
|
|
||||||
|
func (a ByPosition) Len() int { return len(a) }
|
||||||
|
func (a ByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ var (
|
|||||||
|
|
||||||
// Typed errors for product list handler
|
// Typed errors for product list handler
|
||||||
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
||||||
|
|
||||||
|
// Typed errors for menu handler
|
||||||
|
ErrNoRootFound = errors.New("no root found in categories table")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents an error with HTTP status code
|
// Error represents an error with HTTP status code
|
||||||
@@ -135,6 +138,9 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
|||||||
case errors.Is(err, ErrBadPaging):
|
case errors.Is(err, ErrBadPaging):
|
||||||
return i18n.T_(c, "error.err_bad_paging")
|
return i18n.T_(c, "error.err_bad_paging")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrNoRootFound):
|
||||||
|
return i18n.T_(c, "error.no_root_found")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return i18n.T_(c, "error.err_internal_server_error")
|
return i18n.T_(c, "error.err_internal_server_error")
|
||||||
}
|
}
|
||||||
@@ -169,7 +175,8 @@ func GetErrorStatus(err error) int {
|
|||||||
errors.Is(err, ErrBadAttribute),
|
errors.Is(err, ErrBadAttribute),
|
||||||
errors.Is(err, ErrBadField),
|
errors.Is(err, ErrBadField),
|
||||||
errors.Is(err, ErrInvalidXHTML),
|
errors.Is(err, ErrInvalidXHTML),
|
||||||
errors.Is(err, ErrBadPaging):
|
errors.Is(err, ErrBadPaging),
|
||||||
|
errors.Is(err, ErrNoRootFound):
|
||||||
return fiber.StatusBadRequest
|
return fiber.StatusBadRequest
|
||||||
case errors.Is(err, ErrEmailExists):
|
case errors.Is(err, ErrEmailExists):
|
||||||
return fiber.StatusConflict
|
return fiber.StatusConflict
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
package categoriesRepo
|
package categoriesRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
)
|
||||||
|
|
||||||
type UICategoriesRepo interface {
|
type UICategoriesRepo interface {
|
||||||
|
GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoriesRepo struct{}
|
type CategoriesRepo struct{}
|
||||||
@@ -8,3 +14,28 @@ type CategoriesRepo struct{}
|
|||||||
func New() UICategoriesRepo {
|
func New() UICategoriesRepo {
|
||||||
return &CategoriesRepo{}
|
return &CategoriesRepo{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *CategoriesRepo) GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error) {
|
||||||
|
var allCategories []model.ScannedCategory
|
||||||
|
|
||||||
|
err := db.DB.Raw(`
|
||||||
|
SELECT
|
||||||
|
ps_category.id_category AS ID,
|
||||||
|
ps_category_lang.name AS name,
|
||||||
|
ps_category.active AS active,
|
||||||
|
ps_category_shop.position AS position,
|
||||||
|
ps_category.id_parent AS id_parent,
|
||||||
|
ps_category.is_root_category AS is_root_category
|
||||||
|
FROM ps_category
|
||||||
|
LEFT JOIN ps_category_lang
|
||||||
|
ON ps_category_lang.id_category = ps_category.id_category
|
||||||
|
AND ps_category_lang.id_shop = ?
|
||||||
|
AND ps_category_lang.id_lang = ?
|
||||||
|
LEFT JOIN ps_category_shop
|
||||||
|
ON ps_category_shop.id_category = ps_category.id_category
|
||||||
|
AND ps_category_shop.id_shop = ?`,
|
||||||
|
id_shop, id_lang, id_shop).
|
||||||
|
Scan(&allCategories).Error
|
||||||
|
|
||||||
|
return allCategories, err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user