295 lines
7.6 KiB
Go
295 lines
7.6 KiB
Go
package productDescriptionService
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"slices"
|
|
"strings"
|
|
|
|
"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/responseErrors"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type ProductDescriptionService struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func New() *ProductDescriptionService {
|
|
return &ProductDescriptionService{
|
|
db: db.Get(),
|
|
}
|
|
}
|
|
|
|
func isValidXHTML(s string) bool {
|
|
decoder := xml.NewDecoder(strings.NewReader(s))
|
|
hasStartTag := false
|
|
|
|
for {
|
|
tok, err := decoder.Token()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return hasStartTag
|
|
}
|
|
return false
|
|
}
|
|
|
|
if _, ok := tok.(xml.StartElement); ok {
|
|
hasStartTag = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// We assume that any user has access to all product descriptions
|
|
func (s *ProductDescriptionService) GetProductDescription(userID uint, productID uint, productShopID uint, productLangID uint) (*model.ProductDescription, error) {
|
|
var ProductDescription model.ProductDescription
|
|
|
|
err := s.db.
|
|
Table("ps_product_lang").
|
|
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
|
First(&ProductDescription).Error
|
|
if err != nil {
|
|
return nil, fmt.Errorf("database error: %w", err)
|
|
}
|
|
|
|
return &ProductDescription, nil
|
|
}
|
|
|
|
// Updates relevant fields with the "updates" map
|
|
func (s *ProductDescriptionService) SaveProductDescription(userID uint, productID uint, productShopID uint, productLangID uint, updates map[string]string) error {
|
|
// only some fields can be affected
|
|
allowedFields := []string{"description", "description_short", "meta_description", "meta_title", "name", "available_now", "available_later", "usage"}
|
|
for key := range updates {
|
|
if !slices.Contains(allowedFields, key) {
|
|
return responseErrors.ErrBadField
|
|
}
|
|
}
|
|
|
|
// check that fields description, description_short and usage, if they exist, have a valid html format
|
|
mustBeHTML := []string{"description", "description_short", "usage"}
|
|
for i := 0; i < len(mustBeHTML); i++ {
|
|
if text, exists := updates[mustBeHTML[i]]; exists {
|
|
if !isValidXHTML(text) {
|
|
return responseErrors.ErrInvalidHTML
|
|
}
|
|
}
|
|
}
|
|
|
|
record := model.ProductDescription{
|
|
ProductID: productID,
|
|
ShopID: productShopID,
|
|
LangID: productLangID,
|
|
}
|
|
|
|
err := s.db.
|
|
Table("ps_product_lang").
|
|
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
|
FirstOrCreate(&record).Error
|
|
if err != nil {
|
|
return fmt.Errorf("database error: %w", err)
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
return nil
|
|
}
|
|
updatesIface := make(map[string]interface{}, len(updates))
|
|
for k, v := range updates {
|
|
updatesIface[k] = v
|
|
}
|
|
|
|
err = s.db.
|
|
Table("ps_product_lang").
|
|
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, productShopID, productLangID).
|
|
Updates(updatesIface).Error
|
|
if err != nil {
|
|
return fmt.Errorf("database error: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// func (s *ProductDescriptionService) GetRepositoriesForUser(userID uint) ([]uint, error) {
|
|
// var repoIDs []uint
|
|
|
|
// err := s.db.
|
|
// Table("customer_repo_accesses").
|
|
// Where("user_id = ?", userID).
|
|
// Pluck("repo_id", &repoIDs).Error
|
|
// if err != nil {
|
|
// return nil, fmt.Errorf("database error: %w", err)
|
|
// }
|
|
|
|
// return repoIDs, nil
|
|
// }
|
|
|
|
// func (s *RepoService) UserHasAccessToRepo(userID uint, repoID uint) (bool, error) {
|
|
// var repositories []uint
|
|
// var err error
|
|
|
|
// if repositories, err = s.GetRepositoriesForUser(userID); err != nil {
|
|
// return false, err
|
|
// }
|
|
|
|
// if !slices.Contains(repositories, repoID) {
|
|
// return false, responseErrors.ErrInvalidRepoID
|
|
// }
|
|
|
|
// return true, nil
|
|
// }
|
|
|
|
// // Extract all repositories assigned to user with specific id
|
|
// func (s *RepoService) GetYearsForUser(userID uint, repoID uint) ([]uint, error) {
|
|
// if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok {
|
|
// return nil, err
|
|
// }
|
|
|
|
// years, err := s.GetYears(repoID)
|
|
// if err != nil {
|
|
// return nil, fmt.Errorf("database error: %w", err)
|
|
// }
|
|
|
|
// return years, nil
|
|
// }
|
|
|
|
// func (s *RepoService) GetYears(repo uint) ([]uint, error) {
|
|
|
|
// var years []uint
|
|
|
|
// query := `
|
|
// WITH bounds AS (
|
|
// SELECT
|
|
// MIN(to_timestamp(tt.created_unix)) AS min_ts,
|
|
// MAX(to_timestamp(tt.created_unix)) AS max_ts
|
|
// FROM tracked_time tt
|
|
// JOIN issue i ON i.id = tt.issue_id
|
|
// WHERE i.repo_id = ?
|
|
// AND tt.deleted = false
|
|
// )
|
|
// SELECT
|
|
// EXTRACT(YEAR FROM y.year_start)::int AS year
|
|
// FROM bounds
|
|
// CROSS JOIN LATERAL generate_series(
|
|
// date_trunc('year', min_ts),
|
|
// date_trunc('year', max_ts),
|
|
// interval '1 year'
|
|
// ) AS y(year_start)
|
|
// ORDER BY year
|
|
// `
|
|
|
|
// err := db.Get().Raw(query, repo).Find(&years).Error
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// return years, nil
|
|
// }
|
|
|
|
// // Extract all repositories assigned to user with specific id
|
|
// func (s *RepoService) GetQuartersForUser(userID uint, repoID uint, year uint) ([]model.QuarterData, error) {
|
|
// if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok {
|
|
// return nil, err
|
|
// }
|
|
|
|
// response, err := s.GetQuarters(repoID, year)
|
|
// if err != nil {
|
|
// return nil, fmt.Errorf("database error: %w", err)
|
|
// }
|
|
|
|
// return response, nil
|
|
// }
|
|
|
|
// func (s *RepoService) GetQuarters(repo uint, year uint) ([]model.QuarterData, error) {
|
|
// var quarters []model.QuarterData
|
|
|
|
// query := `
|
|
// WITH quarters AS (
|
|
// SELECT
|
|
// make_date(?::int, 1, 1) + (q * interval '3 months') AS quarter_start,
|
|
// q + 1 AS quarter
|
|
// FROM generate_series(0, 3) AS q
|
|
// ),
|
|
// data AS (
|
|
// SELECT
|
|
// EXTRACT(QUARTER FROM to_timestamp(tt.created_unix)) AS quarter,
|
|
// SUM(tt.time) / 3600 AS time
|
|
// FROM tracked_time tt
|
|
// JOIN issue i ON i.id = tt.issue_id
|
|
// JOIN repository r ON i.repo_id = r.id
|
|
// WHERE
|
|
// EXTRACT(YEAR FROM to_timestamp(tt.created_unix)) = ?
|
|
// AND r.id = ?
|
|
// AND tt.deleted = false
|
|
// GROUP BY EXTRACT(QUARTER FROM to_timestamp(tt.created_unix))
|
|
// )
|
|
// SELECT
|
|
// COALESCE(d.time, 0) AS time,
|
|
// CONCAT(EXTRACT(YEAR FROM q.quarter_start)::int, '_Q', q.quarter) AS quarter
|
|
// FROM quarters q
|
|
// LEFT JOIN data d ON d.quarter = q.quarter
|
|
// ORDER BY q.quarter
|
|
// `
|
|
|
|
// err := db.Get().
|
|
// Raw(query, year, year, repo).
|
|
// Find(&quarters).
|
|
// Error
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// return quarters, nil
|
|
// }
|
|
|
|
// func (s *RepoService) GetIssuesForUser(
|
|
// userID uint,
|
|
// repoID uint,
|
|
// year uint,
|
|
// quarter uint,
|
|
// p pagination.Paging,
|
|
// ) (*pagination.Found[model.IssueTimeSummary], error) {
|
|
// if ok, err := s.UserHasAccessToRepo(userID, repoID); !ok {
|
|
// return nil, err
|
|
// }
|
|
|
|
// return s.GetIssues(repoID, year, quarter, p)
|
|
// }
|
|
|
|
// func (s *RepoService) GetIssues(
|
|
// repoId uint,
|
|
// year uint,
|
|
// quarter uint,
|
|
// p pagination.Paging,
|
|
// ) (*pagination.Found[model.IssueTimeSummary], error) {
|
|
|
|
// query := db.Get().
|
|
// Table("issue i").
|
|
// Select(`
|
|
// i.id AS issue_id,
|
|
// i.name AS issue_name,
|
|
// to_timestamp(i.created_unix) AS issue_created_at,
|
|
// ROUND(SUM(tt.time) / 3600.0, 2) AS total_hours_spent
|
|
// `).
|
|
// Joins(`JOIN tracked_time tt ON tt.issue_id = i.id`).
|
|
// Joins(`JOIN "user" u ON u.id = tt.user_id`).
|
|
// Where("i.repo_id = ?", repoId).
|
|
// Where(`
|
|
// EXTRACT(YEAR FROM to_timestamp(tt.created_unix)) = ?
|
|
// AND EXTRACT(QUARTER FROM to_timestamp(tt.created_unix)) = ?
|
|
// `, year, quarter).
|
|
// Group(`
|
|
// i.id,
|
|
// i.name,
|
|
// u.id,
|
|
// u.full_name
|
|
// `).
|
|
// Order("i.created_unix")
|
|
|
|
// result, err := pagination.Paginate[model.IssueTimeSummary](p, query)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
|
|
// return &result, nil
|
|
// }
|