This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
backend/internal/qualification/repository/pg_repository.go

208 lines
5.6 KiB
Go

package repository
import (
"context"
"github.com/Kichiyaki/gopgutil/v10"
"github.com/pkg/errors"
"strings"
"gitea.dwysokinski.me/zdam-egzamin-zawodowy/backend/util/errorutil"
"github.com/go-pg/pg/v10"
"gitea.dwysokinski.me/zdam-egzamin-zawodowy/backend/internal/model"
"gitea.dwysokinski.me/zdam-egzamin-zawodowy/backend/internal/qualification"
)
type PGRepositoryConfig struct {
DB *pg.DB
}
type PGRepository struct {
*pg.DB
}
var _ qualification.Repository = &PGRepository{}
func NewPGRepository(cfg *PGRepositoryConfig) (*PGRepository, error) {
if cfg == nil || cfg.DB == nil {
return nil, errors.New("cfg.DB is required")
}
return &PGRepository{
cfg.DB,
}, nil
}
func (repo *PGRepository) Store(ctx context.Context, input *model.QualificationInput) (*model.Qualification, error) {
item := input.ToQualification()
err := repo.RunInTransaction(ctx, func(tx *pg.Tx) error {
if _, err := tx.
Model(item).
Context(ctx).
Returning("*").
Insert(); err != nil {
return handleInsertAndUpdateError(err)
}
if len(input.AssociateProfession) > 0 {
if err := repo.associateQualificationWithProfession(tx, []int{item.ID}, input.AssociateProfession); err != nil {
return handleInsertAndUpdateError(err)
}
}
return nil
})
return item, err
}
func (repo *PGRepository) UpdateMany(
ctx context.Context,
f *model.QualificationFilter,
input *model.QualificationInput,
) ([]*model.Qualification, error) {
items := make([]*model.Qualification, 0)
err := repo.RunInTransaction(ctx, func(tx *pg.Tx) error {
if input.HasBasicDataToUpdate() {
if _, err := tx.
Model(&model.Qualification{}).
Context(ctx).
Apply(input.ApplyUpdate).
Apply(f.Where).
Update(); err != nil && err != pg.ErrNoRows {
return handleInsertAndUpdateError(err)
}
}
if err := tx.
Model(&items).
Context(ctx).
Apply(f.Where).
Select(); err != nil && err != pg.ErrNoRows {
return handleInsertAndUpdateError(err)
}
qualificationIDs := make([]int, len(items))
for index, item := range items {
qualificationIDs[index] = item.ID
}
if len(qualificationIDs) > 0 {
if len(input.DissociateProfession) > 0 {
_, err := tx.
Model(&model.QualificationToProfession{}).
Where(gopgutil.BuildConditionArray("profession_id"), pg.Array(input.DissociateProfession)).
Where(gopgutil.BuildConditionArray("qualification_id"), pg.Array(qualificationIDs)).
Delete()
if err != nil {
return handleInsertAndUpdateError(err)
}
}
if len(input.AssociateProfession) > 0 {
if err := repo.associateQualificationWithProfession(tx, qualificationIDs, input.AssociateProfession); err != nil {
return handleInsertAndUpdateError(err)
}
}
}
return nil
})
return items, err
}
func (repo *PGRepository) Delete(ctx context.Context, f *model.QualificationFilter) ([]*model.Qualification, error) {
items := make([]*model.Qualification, 0)
if _, err := repo.
Model(&items).
Context(ctx).
Returning("*").
Apply(f.Where).
Delete(); err != nil && err != pg.ErrNoRows {
return nil, errorutil.Wrap(err, messageFailedToDeleteModel)
}
return items, nil
}
func (repo *PGRepository) Fetch(ctx context.Context, cfg *qualification.FetchConfig) ([]*model.Qualification, int, error) {
var err error
items := make([]*model.Qualification, 0)
total := 0
query := repo.
Model(&items).
Context(ctx).
Limit(cfg.Limit).
Offset(cfg.Offset).
Apply(cfg.Filter.Where).
Apply(gopgutil.OrderAppender{
Orders: cfg.Sort,
}.Apply)
if cfg.Count {
total, err = query.SelectAndCount()
} else {
err = query.Select()
}
if err != nil && err != pg.ErrNoRows {
return nil, 0, errorutil.Wrap(err, messageFailedToFetchModel)
}
return items, total, nil
}
func (repo *PGRepository) GetSimilar(ctx context.Context, cfg *qualification.GetSimilarConfig) ([]*model.Qualification, int, error) {
var err error
subquery := repo.
Model(&model.QualificationToProfession{}).
Context(ctx).
Where(gopgutil.BuildConditionEquals("qualification_id"), cfg.QualificationID).
Column("profession_id")
var qualificationIDs []int
err = repo.
Model(&model.QualificationToProfession{}).
Context(ctx).
Column("qualification_id").
With("prof", subquery).
Where(gopgutil.BuildConditionIn("profession_id"), pg.Safe("SELECT profession_id FROM prof")).
Where(gopgutil.BuildConditionNEQ("qualification_id"), cfg.QualificationID).
Select(&qualificationIDs)
if err != nil {
return nil, 0, errorutil.Wrap(err, messageFailedToFetchModel)
}
if len(qualificationIDs) == 0 {
return []*model.Qualification{}, 0, nil
}
return repo.Fetch(ctx, &qualification.FetchConfig{
Sort: cfg.Sort,
Limit: cfg.Limit,
Offset: cfg.Offset,
Filter: &model.QualificationFilter{
ID: qualificationIDs,
},
Count: cfg.Count,
})
}
func (repo *PGRepository) associateQualificationWithProfession(tx *pg.Tx, qualificationIDs, professionIDs []int) error {
var toInsert []*model.QualificationToProfession
for _, professionID := range professionIDs {
for _, qualificationID := range qualificationIDs {
toInsert = append(toInsert, &model.QualificationToProfession{
ProfessionID: professionID,
QualificationID: qualificationID,
})
}
}
_, err := tx.Model(&toInsert).OnConflict("DO NOTHING").Insert()
return err
}
func handleInsertAndUpdateError(err error) error {
if strings.Contains(err.Error(), "name") {
return errorutil.Wrap(err, messageNameIsAlreadyTaken)
} else if strings.Contains(err.Error(), "code") || strings.Contains(err.Error(), "slug") {
return errorutil.Wrap(err, messageCodeIsAlreadyTaken)
}
return errorutil.Wrap(err, messageFailedToSaveModel)
}