Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12 add api documentation #7

Open
wants to merge 87 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
4d7a7dd
remove core limitation
matheuspolitano Dec 23, 2024
6c29e2f
fix main bugs
matheuspolitano Dec 24, 2024
1601264
fix main bugs
matheuspolitano Dec 24, 2024
1310b7b
down go version
matheuspolitano Dec 24, 2024
aa08816
add release file
matheuspolitano Dec 24, 2024
8e08e34
add release info in readme
matheuspolitano Dec 24, 2024
70f5b75
Merge pull request #1 from matheuspolitano/1-fix-main-bugs
matheuspolitano Dec 24, 2024
0982ac0
add new pipeline
matheuspolitano Dec 24, 2024
011b3a8
Merge pull request #2 from matheuspolitano/1-fix-main-bugs-1
matheuspolitano Dec 24, 2024
fee950d
fix pipeline
matheuspolitano Dec 24, 2024
6df7c14
Merge pull request #3 from matheuspolitano/1-fix-main-bugs-2
matheuspolitano Dec 24, 2024
3690243
Add new release file release_20241224104515.toml
github-actions[bot] Dec 24, 2024
4285907
improve the pipeline
matheuspolitano Dec 24, 2024
015183f
update release
matheuspolitano Dec 24, 2024
9a12574
Merge pull request #4 from matheuspolitano/2-improve-the-pipeline
matheuspolitano Dec 24, 2024
e8914ac
Add new release file release_20241224105329.toml
github-actions[bot] Dec 24, 2024
a363d78
pipeline adjust
matheuspolitano Dec 24, 2024
c9c86ad
Merge pull request #5 from matheuspolitano/3-remove-release-file
matheuspolitano Dec 24, 2024
d51f15e
Add new release file release_20241224111005.toml
github-actions[bot] Dec 24, 2024
d766a84
improve errors messages
matheuspolitano Dec 24, 2024
375a284
ädd local env and extras check in config
matheuspolitano Dec 24, 2024
a4e8ec9
change main go file
matheuspolitano Dec 24, 2024
497f3c8
add config
matheuspolitano Dec 24, 2024
4b0ed73
add config test
matheuspolitano Dec 24, 2024
e904542
run in sequence de config test and improve the config
matheuspolitano Dec 24, 2024
29e68c4
fix type error in port
matheuspolitano Dec 24, 2024
314338f
add note in release
matheuspolitano Dec 24, 2024
90991ca
improve task detail
matheuspolitano Dec 24, 2024
858c708
Merge pull request #6 from matheuspolitano/5-improve-config
matheuspolitano Dec 24, 2024
55ba43f
Add new release file release_20241224141902.toml
github-actions[bot] Dec 24, 2024
8a9556d
update readme
matheuspolitano Dec 24, 2024
049a9af
update readme
matheuspolitano Dec 24, 2024
0a5b0ce
update readme
matheuspolitano Dec 24, 2024
be3a5b7
update readme
matheuspolitano Dec 24, 2024
76b5c7f
add myduration
matheuspolitano Dec 25, 2024
6fc90b0
add test
matheuspolitano Dec 25, 2024
29092dc
add release
matheuspolitano Dec 25, 2024
ecb6071
Merge pull request #8 from matheuspolitano/7-improve-time-alive
matheuspolitano Dec 25, 2024
3e48f98
Add new release file release_20241225114904.toml
github-actions[bot] Dec 25, 2024
650acee
improve main.go and server start and shutdown
matheuspolitano Dec 25, 2024
dadf977
improve readme
matheuspolitano Dec 25, 2024
37eecea
Merge pull request #9 from matheuspolitano/6-improve-main.go
matheuspolitano Dec 25, 2024
81a767c
Add new release file release_20241225141728.toml
github-actions[bot] Dec 25, 2024
f6d55be
add env as type
matheuspolitano Dec 25, 2024
c99942f
add new pacakge and set new test
matheuspolitano Dec 25, 2024
d7b3d96
add production mod
matheuspolitano Dec 25, 2024
a94b47f
remove useless comments
matheuspolitano Dec 25, 2024
798ec5a
add max age
matheuspolitano Dec 25, 2024
e2dfaa5
improve response
matheuspolitano Dec 25, 2024
aa4a2f6
improve config and add new config
matheuspolitano Dec 25, 2024
1e16f01
update dockerimage
matheuspolitano Dec 25, 2024
4c3e893
add in readme
matheuspolitano Dec 25, 2024
2ad6d1f
add release
matheuspolitano Dec 25, 2024
aa6389b
update go version
matheuspolitano Dec 25, 2024
d355e9b
update pipeline and go version
matheuspolitano Dec 25, 2024
e1b534d
ignore scripts
matheuspolitano Dec 26, 2024
57f302b
improve release pipeline
matheuspolitano Dec 26, 2024
215b9c5
add ignore
matheuspolitano Dec 26, 2024
6a874e9
improve the validate release
matheuspolitano Dec 26, 2024
088cedf
improve release
matheuspolitano Dec 26, 2024
da84809
add response
matheuspolitano Dec 26, 2024
bdc9bbe
improve format
matheuspolitano Dec 26, 2024
49429e7
update release
matheuspolitano Dec 26, 2024
79b53c0
fix test
matheuspolitano Dec 26, 2024
c2db65b
Merge pull request #10 from matheuspolitano/7-improve-gin-api
matheuspolitano Dec 26, 2024
0b2c8a2
Add new release file release_20241226171013.toml
github-actions[bot] Dec 26, 2024
f203a84
improve memdb
matheuspolitano Dec 26, 2024
1cea555
Merge pull request #11 from matheuspolitano/8-improve-repository
matheuspolitano Dec 26, 2024
a8acdba
Add new release file release_20241226174317.toml
github-actions[bot] Dec 26, 2024
caa30ad
add docker
matheuspolitano Dec 26, 2024
69069d6
add release
matheuspolitano Dec 26, 2024
aad42e9
add release
matheuspolitano Dec 26, 2024
80eccc2
Merge pull request #12 from matheuspolitano/9-setup-docker
matheuspolitano Dec 27, 2024
c245e41
Release: Add release_20241227000208.toml
github-actions[bot] Dec 27, 2024
64c930a
cicd adjust
matheuspolitano Dec 27, 2024
ebc0cf5
Merge pull request #13 from matheuspolitano/10-migrate-deploy-file
matheuspolitano Dec 27, 2024
dedcac4
Release: Add release_20241227122723.toml
github-actions[bot] Dec 27, 2024
5166895
11 improve service #major (#14)
matheuspolitano Dec 27, 2024
06ac48a
Release: Add release_20241227124702.toml
github-actions[bot] Dec 27, 2024
cde0067
add doc
matheuspolitano Dec 27, 2024
e5c9e4b
improve readme
matheuspolitano Dec 27, 2024
9fa2a51
improve readme
matheuspolitano Dec 27, 2024
12d5bb3
improve readme
matheuspolitano Dec 27, 2024
10af987
improve readme
matheuspolitano Dec 27, 2024
89cbe2a
improve readme
matheuspolitano Dec 27, 2024
94d8b9a
add latest push in docker
matheuspolitano Dec 27, 2024
5704f8e
go mod tidy
matheuspolitano Dec 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
11 improve service #major (#14)
* improve service

* add service improvements

* improve the models
  • Loading branch information
matheuspolitano authored Dec 27, 2024
commit 5166895d744a0b8c357d6f9013f2332ee60d6ce2
8 changes: 4 additions & 4 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
)

type Service interface {
ApplyCoupon(entity.Basket, string) (*entity.Basket, error)
CreateCoupon(int, string, int) (string, error)
GetCoupons([]string) ([]entity.Coupon, error)
ApplyCoupon(*entity.Basket, string) error
CreateCoupon(discount int, code string, minBasketValue int) (*entity.Coupon, error)
GetCoupons([]string) ([]*entity.Coupon, error)
}

// Config store main api settings
Expand All @@ -37,7 +37,7 @@ type API struct {
cfg Config
}

func New[T Service](cfg Config, svc T) (*API, error) {
func New(cfg Config, svc Service) (*API, error) {
var logger *zap.Logger
var err error

Expand Down
22 changes: 12 additions & 10 deletions internal/api/coupon.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,44 @@ func (a *API) Apply(c *gin.Context) {
if err := c.ShouldBindJSON(&apiReq); err != nil {
return
}
basket, err := a.svc.ApplyCoupon(apiReq.Basket, apiReq.Code)
err := a.svc.ApplyCoupon(&apiReq.Basket, apiReq.Code)
if err != nil {
SendError(c, "error to apply the coupon", err.Error(), http.StatusBadRequest)
return
}

c.JSON(http.StatusOK, basket)
SendSuccess(c, "coupon applied successfully", apiReq.Basket)
}

func (a *API) Create(c *gin.Context) {
apiReq := Coupon{}
apiReq := CouponRequest{}
if err := c.ShouldBindJSON(&apiReq); err != nil {
SendError(c, "error to read the body", err.Error(), http.StatusBadRequest)
return
}
id, err := a.svc.CreateCoupon(apiReq.Discount, apiReq.Code, apiReq.MinBasketValue)
coupon, err := a.svc.CreateCoupon(apiReq.Discount, apiReq.Code, apiReq.MinBasketValue)
if err != nil {
SendError(c, "error to create coupon", err.Error(), http.StatusBadRequest)
return
}
SendSuccess(c, fmt.Sprintf("Coupon %s created successfully", id), nil)
SendSuccess(c, fmt.Sprintf("coupon %s created successfully", coupon.ID), coupon)
}

func (a *API) Get(c *gin.Context) {
codes := c.Query("codes")
if codes == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'codes' is required"})
SendError(c, "query parameter 'codes' is required", "", http.StatusBadRequest)
return
}
codeList := strings.Split(codes, ",")
if len(codeList) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'codes' cannot be empty"})
SendError(c, "query parameter 'codes' cannot be empty", "", http.StatusBadRequest)
return
}

coupons, err := a.svc.GetCoupons(codeList)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "failed not found coupons"})
SendError(c, "error to found all coupons", err.Error(), http.StatusNotFound)
return
}
c.JSON(http.StatusOK, coupons)
SendSuccess(c, "found all coupons", coupons)
}
8 changes: 4 additions & 4 deletions internal/api/entity/Coupons.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package entity

type Coupon struct {
Discount int `json:"discount"`
Code string `json:"code"`
MinBasketValue int `json:"min_basket_value"`
type CouponRequest struct {
Discount int `json:"discount" binding:"required"`
Code string `json:"code" binding:"required"`
MinBasketValue int `json:"min_basket_value" binding:"required"`
}
4 changes: 2 additions & 2 deletions internal/api/entity/applicationRequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package entity
import "coupon_service/internal/service/entity"

type ApplicationRequest struct {
Code string
Basket entity.Basket
Code string `json:"code" binding:"required"`
Basket entity.Basket `json:"basket" binding:"required"`
}
5 changes: 0 additions & 5 deletions internal/api/entity/couponRequest.go

This file was deleted.

14 changes: 0 additions & 14 deletions internal/repository/memdb/memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,3 @@ func (r *Repository) Save(coupon *entity.Coupon) error {
r.entries[coupon.Code] = &couponCopy
return nil
}

// Delete removes a Coupon from the repository by its code.
// It returns an error if the coupon does not exist.
func (r *Repository) Delete(code string) error {
r.mu.Lock()
defer r.mu.Unlock()

if _, exists := r.entries[code]; !exists {
return ErrCouponNotFound
}

delete(r.entries, code)
return nil
}
12 changes: 0 additions & 12 deletions internal/repository/memdb/memdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,8 @@ func TestRepository_Delete(t *testing.T) {
err := repo.Save(coupon)
assert.NoError(t, err, "Expected no error when saving a valid coupon")

err = repo.Delete("WINTER30")
assert.NoError(t, err, "Expected no error when deleting an existing coupon")

// Verify that the coupon is deleted
_, err = repo.FindByCode("WINTER30")
assert.Error(t, err, "Expected an error when finding a deleted coupon")
assert.Equal(t, ErrCouponNotFound, err, "Error should be ErrCouponNotFound")
})

t.Run("Attempt to delete a non-existent coupon", func(t *testing.T) {
err := repo.Delete("NONEXISTENT")
assert.Error(t, err, "Expected an error when deleting a non-existent coupon")
assert.Equal(t, ErrCouponNotFound, err, "Error should be ErrCouponNotFound")
})
}

func TestRepository_Concurrency(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions internal/service/entity/basket.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

type Basket struct {
Value int
AppliedDiscount int
ApplicationSuccessful bool
Value int `json:"Value" binding:"required"`
AppliedDiscount int `json:"applied_discount,omitempty"`
ApplicationSuccessful bool `json:"application_successful,omitempty"`
}
8 changes: 4 additions & 4 deletions internal/service/entity/coupon.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package entity

type Coupon struct {
ID string
Code string
Discount int
MinBasketValue int
ID string `json:"id"`
Code string `json:"code"`
Discount int `json:"discount"`
MinBasketValue int `json:"min_basket_value"`
}
29 changes: 29 additions & 0 deletions internal/service/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package service

import (
"errors"
"fmt"
)

// Custom errors for better error handling.
var (
ErrApplyDiscount = errors.New("cannot apply discount to a basket of non-positive value")
ErrCouponDiscountValue = errors.New("discount must be a positive integer")
ErrCouponMinBasketValue = errors.New("minBasketValue cannot be negative")
ErrCouponDiscountTooBig = errors.New("discount cannot be higher minBasketValue")
ErrCouponCodeAlreadyExist = errors.New("coupon code already used for another coupon")
)

// ErrApplyDiscountLessMin Custom error to apply function
type ErrApplyDiscountLessMin struct {
MinValue int
Current int
}

// Error return the message with the value
func (e *ErrApplyDiscountLessMin) Error() string {
return fmt.Sprintf(
"cannot apply discount: value %d did not reach the minimum %d",
e.Current, e.MinValue,
)
}
85 changes: 60 additions & 25 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,100 @@ import (
"github.com/google/uuid"
)

// Repository interface to memdb repository
type Repository interface {
FindByCode(string) (*Coupon, error)
Save(*Coupon) error
}

// Service manage application features
type Service struct {
repo Repository
}

func New(repo Repository) Service {
return Service{
// New create a new Service
func New(repo Repository) *Service {
return &Service{
repo: repo,
}
}

func (s Service) ApplyCoupon(basket Basket, code string) (b *Basket, e error) {
b = &basket
// ApplyCoupon in the basket provided
// It returns an error if code coupon not exist and basket value must be positive value
func (s *Service) ApplyCoupon(basket *Basket, code string) error {
coupon, err := s.repo.FindByCode(code)
if err != nil {
return nil, err
return err
}

if b.Value > 0 {
b.AppliedDiscount = coupon.Discount
b.ApplicationSuccessful = true
if basket.Value <= 0 {
return ErrApplyDiscount
}
if b.Value == 0 {
return

if basket.Value < coupon.MinBasketValue {
return &ErrApplyDiscountLessMin{
MinValue: coupon.MinBasketValue,
Current: basket.Value,
}
}

return nil, fmt.Errorf("Tried to apply discount to negative value")
basket.AppliedDiscount = coupon.Discount
basket.ApplicationSuccessful = true

return nil
}

func (s Service) CreateCoupon(discount int, code string, minBasketValue int) (string, error) {
coupon := Coupon{
Discount: discount,
// CreateCoupon a new coupon
// It returns an error if discount not be a positive number and minBasketValue cant not be negative
// It returns an error if discount be higher that min basket
func (s *Service) CreateCoupon(discount int, code string, minBasketValue int) (*Coupon, error) {
if discount <= 0 {
return nil, ErrCouponDiscountValue
}

if minBasketValue < 0 {
return nil, ErrCouponMinBasketValue
}

if discount > minBasketValue {
return nil, ErrCouponDiscountTooBig
}

if _, err := s.repo.FindByCode(code); err == nil {
return nil, ErrCouponCodeAlreadyExist
}

coupon := &Coupon{
ID: uuid.NewString(),
Code: code,
Discount: discount,
MinBasketValue: minBasketValue,
ID: uuid.NewString(),
}

if err := s.repo.Save(&coupon); err != nil {
return "", err
if err := s.repo.Save(coupon); err != nil {
return nil, fmt.Errorf("failed to save coupon: %w", err)
}
return coupon.ID, nil
return coupon, nil
}

func (s Service) GetCoupons(codes []string) ([]Coupon, error) {
coupons := make([]Coupon, 0, len(codes))
var e error
// GetCoupons return a list of coupons based on the codes provided
// It returns an error if case one of the code does not exist will
func (s *Service) GetCoupons(codes []string) ([]*Coupon, error) {
coupons := make([]*Coupon, 0, len(codes))
var errs []error

for idx, code := range codes {
for _, code := range codes {
coupon, err := s.repo.FindByCode(code)
if err != nil {
e = fmt.Errorf("code: %s, index: %d", code, idx)
errs = append(errs, fmt.Errorf("coupon code: %s, err: %s", code, err.Error()))
continue
}
coupons = append(coupons, *coupon)
coupons = append(coupons, coupon)
}

if len(errs) > 0 {
return coupons, fmt.Errorf("one or more errors occurred: %v", errs)
}

return coupons, e
return coupons, nil
}
Loading