From f49dfc5c43b54a3502240bf42d44fbd9f9e14f7b Mon Sep 17 00:00:00 2001 From: Kichiyaki Date: Sat, 27 Feb 2021 11:33:16 +0100 Subject: [PATCH] profession: add pg_repository, add a new package errorutils --- internal/models/profession.go | 21 +++++ internal/models/qualification.go | 8 ++ internal/models/question.go | 4 + internal/models/user.go | 4 + internal/profession/repository.go | 22 +++++ internal/profession/repository/message.go | 8 ++ .../profession/repository/pg_repository.go | 93 +++++++++++++++++++ internal/profession/usecase.go | 17 ++++ pkg/mode/mode.go | 4 +- pkg/utils/error/error.go | 19 ++++ 10 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 internal/profession/repository.go create mode 100644 internal/profession/repository/message.go create mode 100644 internal/profession/repository/pg_repository.go create mode 100644 internal/profession/usecase.go create mode 100644 pkg/utils/error/error.go diff --git a/internal/models/profession.go b/internal/models/profession.go index 99e1d0d..c2afc6a 100644 --- a/internal/models/profession.go +++ b/internal/models/profession.go @@ -40,6 +40,10 @@ type ProfessionInput struct { Description *string `json:"description,omitempty" xml:"description" gqlgen:"description"` } +func (input *ProfessionInput) IsEmpty() bool { + return input == nil && input.Name == nil && input.Description == nil +} + func (input *ProfessionInput) ToProfession() *Profession { p := &Profession{} if input.Name != nil { @@ -51,6 +55,19 @@ func (input *ProfessionInput) ToProfession() *Profession { return p } +func (input *ProfessionInput) ApplyUpdate(q *orm.Query) (*orm.Query, error) { + if !input.IsEmpty() { + if input.Name != nil { + q.Set("name = ?", *input.Name) + } + if input.Description != nil { + q.Set("description = ?", *input.Description) + } + } + + return q, nil +} + type ProfessionFilter struct { ID []int `gqlgen:"id" json:"id" xml:"id"` IDNEQ []int `gqlgen:"idNEQ" json:"idNEQ" xml:"idNEQ"` @@ -74,6 +91,10 @@ type ProfessionFilter struct { } func (f *ProfessionFilter) WhereWithAlias(q *orm.Query, alias string) (*orm.Query, error) { + if f == nil { + return q, nil + } + if !isZero(f.ID) { q = q.Where(sqlutils.BuildConditionArray(sqlutils.AddAliasToColumnName("id", alias)), pg.Array(f.ID)) } diff --git a/internal/models/qualification.go b/internal/models/qualification.go index 789c1f6..2ecfa90 100644 --- a/internal/models/qualification.go +++ b/internal/models/qualification.go @@ -79,6 +79,10 @@ type QualificationFilterOr struct { } func (f *QualificationFilterOr) WhereWithAlias(q *orm.Query, alias string) *orm.Query { + if f == nil { + return q + } + q = q.WhereGroup(func(q *orm.Query) (*orm.Query, error) { if !isZero(f.NameMATCH) { q = q.Where(sqlutils.BuildConditionMatch(sqlutils.AddAliasToColumnName("name", alias)), f.NameMATCH) @@ -134,6 +138,10 @@ type QualificationFilter struct { } func (f *QualificationFilter) WhereWithAlias(q *orm.Query, alias string) (*orm.Query, error) { + if f == nil { + return q, nil + } + if !isZero(f.ID) { q = q.Where(sqlutils.BuildConditionArray(sqlutils.AddAliasToColumnName("id", alias)), pg.Array(f.ID)) } diff --git a/internal/models/question.go b/internal/models/question.go index 117192b..7d8235d 100644 --- a/internal/models/question.go +++ b/internal/models/question.go @@ -124,6 +124,10 @@ type QuestionFilter struct { } func (f *QuestionFilter) WhereWithAlias(q *orm.Query, alias string) (*orm.Query, error) { + if f == nil { + return q, nil + } + if !isZero(f.ID) { q = q.Where(sqlutils.BuildConditionArray(sqlutils.AddAliasToColumnName("id", alias)), pg.Array(f.ID)) } diff --git a/internal/models/user.go b/internal/models/user.go index aceb670..394b24c 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -122,6 +122,10 @@ type UserFilter struct { } func (f *UserFilter) WhereWithAlias(q *orm.Query, alias string) (*orm.Query, error) { + if f == nil { + return q, nil + } + if !isZero(f.ID) { q = q.Where(sqlutils.BuildConditionArray(sqlutils.AddAliasToColumnName("id", alias)), pg.Array(f.ID)) } diff --git a/internal/profession/repository.go b/internal/profession/repository.go new file mode 100644 index 0000000..39959c3 --- /dev/null +++ b/internal/profession/repository.go @@ -0,0 +1,22 @@ +package profession + +import ( + "context" + + "github.com/zdam-egzamin-zawodowy/backend/internal/models" +) + +type FetchConfig struct { + Filter *models.ProfessionFilter + Offset int + Limit int + Sort []string + Count bool +} + +type Repository interface { + Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error) + Update(ctx context.Context, f *models.ProfessionFilter, input *models.ProfessionInput) ([]*models.Profession, error) + Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error) + Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Profession, int, error) +} diff --git a/internal/profession/repository/message.go b/internal/profession/repository/message.go new file mode 100644 index 0000000..6d312dd --- /dev/null +++ b/internal/profession/repository/message.go @@ -0,0 +1,8 @@ +package repository + +const ( + nameIsAlreadyTaken = "Istnieje już zawód o podanej nazwie." + failedToSaveModel = "Wystąpił błąd podczas zapisywania zawodu, prosimy spróbować później." + failedToDeleteModel = "Wystąpił błąd podczas usuwania zawodu, prosimy spróbować później." + failedToFetchModel = "Wystąpił błąd podczas pobierania zawodów, prosimy spróbować później." +) diff --git a/internal/profession/repository/pg_repository.go b/internal/profession/repository/pg_repository.go new file mode 100644 index 0000000..2f6d198 --- /dev/null +++ b/internal/profession/repository/pg_repository.go @@ -0,0 +1,93 @@ +package repository + +import ( + "context" + "strings" + + errorutils "github.com/zdam-egzamin-zawodowy/backend/pkg/utils/error" + + "github.com/go-pg/pg/v10" + "github.com/zdam-egzamin-zawodowy/backend/internal/models" + "github.com/zdam-egzamin-zawodowy/backend/internal/profession" +) + +type pgRepository struct { + *pg.DB +} + +type PGRepositoryConfig struct { + db *pg.DB +} + +func NewPGRepository(cfg PGRepositoryConfig) profession.Repository { + return &pgRepository{ + cfg.db, + } +} + +func (repo *pgRepository) Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error) { + item := input.ToProfession() + if _, err := repo. + Model(item). + Context(ctx). + Returning("*"). + Insert(); err != nil { + if strings.Contains(err.Error(), "name") { + return nil, errorutils.Wrap(err, nameIsAlreadyTaken) + } + return nil, errorutils.Wrap(err, failedToSaveModel) + } + return item, nil +} + +func (repo *pgRepository) Update(ctx context.Context, f *models.ProfessionFilter, input *models.ProfessionInput) ([]*models.Profession, error) { + items := []*models.Profession{} + if _, err := repo. + Model(&items). + Context(ctx). + Returning("*"). + Apply(input.ApplyUpdate). + Apply(f.Where). + Update(); err != nil { + if strings.Contains(err.Error(), "name") { + return nil, errorutils.Wrap(err, nameIsAlreadyTaken) + } + return nil, errorutils.Wrap(err, failedToSaveModel) + } + return items, nil +} + +func (repo *pgRepository) Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error) { + items := []*models.Profession{} + if _, err := repo. + Model(&items). + Context(ctx). + Returning("*"). + Apply(f.Where). + Delete(); err != nil { + return nil, errorutils.Wrap(err, failedToDeleteModel) + } + return items, nil +} + +func (repo *pgRepository) Fetch(ctx context.Context, cfg *profession.FetchConfig) ([]*models.Profession, int, error) { + var err error + items := []*models.Profession{} + total := 0 + query := repo. + Model(&items). + Context(ctx). + Limit(cfg.Limit). + Offset(cfg.Offset). + Apply(cfg.Filter.Where) + + if cfg.Count { + total, err = query.SelectAndCount() + } else { + err = query.Select() + } + if err != nil && err != pg.ErrNoRows { + return nil, 0, errorutils.Wrap(err, failedToFetchModel) + } + return items, total, nil +} diff --git a/internal/profession/usecase.go b/internal/profession/usecase.go new file mode 100644 index 0000000..e7ead26 --- /dev/null +++ b/internal/profession/usecase.go @@ -0,0 +1,17 @@ +package profession + +import ( + "context" + + "github.com/zdam-egzamin-zawodowy/backend/internal/models" +) + +type Usecase interface { + Store(ctx context.Context, input *models.ProfessionInput) (*models.Profession, error) + UpdateOne(ctx context.Context, id int, input *models.ProfessionInput) (*models.Profession, error) + UpdateMany(ctx context.Context, f *models.ProfessionFilter, input *models.ProfessionInput) ([]*models.Profession, error) + Delete(ctx context.Context, f *models.ProfessionFilter) ([]*models.Profession, error) + Fetch(ctx context.Context, cfg *FetchConfig) ([]*models.Profession, int, error) + GetByID(ctx context.Context, id int) (*models.Profession, error) + GetBySlug(ctx context.Context, slug string) (*models.Profession, error) +} diff --git a/pkg/mode/mode.go b/pkg/mode/mode.go index cd9f4fc..676bfcf 100644 --- a/pkg/mode/mode.go +++ b/pkg/mode/mode.go @@ -5,7 +5,7 @@ import ( ) const ( - EnvMode = "MODE" + EnvKey = "MODE" DevelopmentMode = "development" ProductionMode = "production" TestMode = "test" @@ -14,7 +14,7 @@ const ( var mode = DevelopmentMode func init() { - Set(os.Getenv(EnvMode)) + Set(os.Getenv(EnvKey)) } func Set(value string) { diff --git a/pkg/utils/error/error.go b/pkg/utils/error/error.go new file mode 100644 index 0000000..b8b512b --- /dev/null +++ b/pkg/utils/error/error.go @@ -0,0 +1,19 @@ +package errorutils + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/zdam-egzamin-zawodowy/backend/pkg/mode" +) + +func Wrap(details error, message string) error { + return Wrapf(details, message) +} + +func Wrapf(details error, message string, args ...interface{}) error { + if mode.Get() != mode.ProductionMode { + return errors.Wrapf(details, message, args...) + } + return fmt.Errorf(message) +}