Skip to content

Commit

Permalink
Get_sale_order handler and use case
Browse files Browse the repository at this point in the history
  • Loading branch information
kiaplayer committed Jan 7, 2024
1 parent d51a2c7 commit 21ccaa9
Show file tree
Hide file tree
Showing 12 changed files with 481 additions and 9 deletions.
9 changes: 7 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"github.com/kiaplayer/clean-architecture-example/internal/adapters/repositories/sale_order"
saleorderservice "github.com/kiaplayer/clean-architecture-example/internal/domain/service/sale_order"
createsaleorderusecase "github.com/kiaplayer/clean-architecture-example/internal/domain/use_case/create_sale_order"
getsaleorderusecase "github.com/kiaplayer/clean-architecture-example/internal/domain/use_case/get_sale_order"
"github.com/kiaplayer/clean-architecture-example/internal/handlers/create_sale_order"
"github.com/kiaplayer/clean-architecture-example/internal/handlers/get_sale_order"
"github.com/kiaplayer/clean-architecture-example/pkg/generators"
"github.com/kiaplayer/clean-architecture-example/pkg/storage/db"
)
Expand All @@ -19,7 +21,10 @@ func main() {
numberGenerator := generators.NewNumberGenerator()
saleOrderRepo := sale_order.NewRepository(conn)
saleOrderService := saleorderservice.NewService(saleOrderRepo)
useCase := createsaleorderusecase.NewUseCase(timeGenerator, numberGenerator, saleOrderService)

create_sale_order.NewHandler(useCase, transactor)
create_sale_order.NewHandler(
createsaleorderusecase.NewUseCase(timeGenerator, numberGenerator, saleOrderService),
transactor,
)
get_sale_order.NewHandler(getsaleorderusecase.NewUseCase(saleOrderService))
}
15 changes: 15 additions & 0 deletions internal/domain/service/sale_order/mocks/service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions internal/domain/service/sale_order/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

type repository interface {
CreateOrder(ctx context.Context, order *document.SaleOrder) (*document.SaleOrder, error)
GetByID(ctx context.Context, id uint64) (*document.SaleOrder, error)
}

type Service struct {
Expand All @@ -28,13 +29,24 @@ func (s *Service) CreateOrder(ctx context.Context, order *document.SaleOrder) (*
if err != nil {
return order, err
}
return s.repository.CreateOrder(ctx, order)
// TODO: Additinal logic (reserve, email...)
savedSaleOrder, err := s.repository.CreateOrder(ctx, order)
if err != nil {
return nil, err
}
// TODO: Additinal logic goes here
// - Reserve products
// - Send emails to customer and manager (via pgq)
// - etc
return savedSaleOrder, nil
}

func (s *Service) ValidateOrder(order *document.SaleOrder) (*document.SaleOrder, error) {
if !slices.Contains(document.ValidStatuses, order.Status) {
return order, fmt.Errorf("bad status: %d", order.Status)
return nil, fmt.Errorf("bad status: %d", order.Status)
}
return order, nil
}

func (s *Service) GetOrderByID(ctx context.Context, id uint64) (*document.SaleOrder, error) {
return s.repository.GetByID(ctx, id)
}
31 changes: 30 additions & 1 deletion internal/domain/service/sale_order/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestCreateOrder_ValidateError(t *testing.T) {
actualSaleOrder, actualErr := service.CreateOrder(ctx, saleOrder)

// assert
assert.Equal(t, saleOrder, actualSaleOrder)
assert.Nil(t, actualSaleOrder)
assert.ErrorContains(t, actualErr, "bad status")
}

Expand Down Expand Up @@ -95,3 +95,32 @@ func TestCreateOrder_CreateError(t *testing.T) {
assert.Nil(t, actualSaleOrder)
assert.ErrorContains(t, actualErr, createErr.Error())
}

func TestGetOrderByID_Success(t *testing.T) {
// arrange
ctrl := gomock.NewController(t)
ctx := context.Background()

repositoryMock := mocks.NewMockrepository(ctrl)

service := NewService(repositoryMock)

saleOrder := &document.SaleOrder{
Document: document.Document{
ID: 1,
Date: time.Now().Truncate(time.Second),
Number: "0001",
},
}

repositoryMock.EXPECT().
GetByID(ctx, saleOrder.ID).
Return(saleOrder, nil)

// act
actualSaleOrder, actualErr := service.GetOrderByID(ctx, saleOrder.ID)

// assert
assert.NoError(t, actualErr)
assert.Equal(t, saleOrder, actualSaleOrder)
}
51 changes: 51 additions & 0 deletions internal/domain/use_case/get_sale_order/mocks/use_case.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions internal/domain/use_case/get_sale_order/use_case.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:generate mockgen -package=$GOPACKAGE -source=$GOFILE -destination=mocks/$GOFILE
package get_sale_order

import (
"context"

"github.com/kiaplayer/clean-architecture-example/internal/domain/entity/document"
)

type saleOrderService interface {
GetOrderByID(ctx context.Context, id uint64) (*document.SaleOrder, error)
}

type UseCase struct {
saleOrderService saleOrderService
}

func NewUseCase(sos saleOrderService) *UseCase {
return &UseCase{
saleOrderService: sos,
}
}

func (u *UseCase) Handle(ctx context.Context, id uint64) (saleOrder *document.SaleOrder, err error) {
return u.saleOrderService.GetOrderByID(ctx, id)
}
69 changes: 69 additions & 0 deletions internal/domain/use_case/get_sale_order/use_case_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package get_sale_order

import (
"context"
"errors"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

"github.com/kiaplayer/clean-architecture-example/internal/domain/entity/document"
mocks "github.com/kiaplayer/clean-architecture-example/internal/domain/use_case/get_sale_order/mocks"
)

func TestHandle_Success(t *testing.T) {
// arrange
ctrl := gomock.NewController(t)
ctx := context.Background()

saleOrderServiceMock := mocks.NewMocksaleOrderService(ctrl)

useCase := NewUseCase(saleOrderServiceMock)

saleOrder := &document.SaleOrder{
Document: document.Document{
ID: 1,
},
}

saleOrderServiceMock.EXPECT().
GetOrderByID(ctx, saleOrder.ID).
Return(saleOrder, nil)

// act
actualSaleOrder, actualErr := useCase.Handle(ctx, saleOrder.ID)

// assert
assert.NoError(t, actualErr)
assert.Equal(t, saleOrder, actualSaleOrder)
}

func TestHandle_Error(t *testing.T) {
// arrange
ctrl := gomock.NewController(t)
ctx := context.Background()

saleOrderServiceMock := mocks.NewMocksaleOrderService(ctrl)

useCase := NewUseCase(saleOrderServiceMock)

saleOrder := &document.SaleOrder{
Document: document.Document{
ID: 1,
},
}

getErr := errors.New("get error")

saleOrderServiceMock.EXPECT().
GetOrderByID(ctx, saleOrder.ID).
Return(nil, getErr)

// act
actualSaleOrder, actualErr := useCase.Handle(ctx, saleOrder.ID)

// assert
assert.Nil(t, actualSaleOrder)
assert.ErrorContains(t, actualErr, getErr.Error())
}
2 changes: 1 addition & 1 deletion internal/handlers/create_sale_order/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (h *Handler) Handle(ctx context.Context, writer http.ResponseWriter, reques
saleOrderForCast, err := h.transactor.RunInTx(ctx, func(ctx context.Context) (any, error) {
return h.useCase.Handle(ctx, products, company, customer, appendUser)
})
if err != nil || saleOrderForCast == nil {
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}
Expand Down
74 changes: 74 additions & 0 deletions internal/handlers/get_sale_order/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//go:generate mockgen -package=$GOPACKAGE -source=$GOFILE -destination=mocks/$GOFILE
package get_sale_order

import (
"context"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/kiaplayer/clean-architecture-example/internal/domain/entity/document"
)

type useCase interface {
Handle(
ctx context.Context,
id uint64,
) (saleOrder *document.SaleOrder, err error)
}

type Handler struct {
useCase useCase
}

func NewHandler(u useCase) *Handler {
return &Handler{
useCase: u,
}
}

func (h *Handler) Handle(ctx context.Context, writer http.ResponseWriter, request *http.Request) {
err := h.checkAccess(request)
if err != nil {
writer.WriteHeader(http.StatusForbidden)
_, _ = writer.Write([]byte(err.Error()))
return
}

saleOrderID, err := h.validateAndPrepare(request)
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
_, _ = writer.Write([]byte(err.Error()))
return
}

saleOrder, err := h.useCase.Handle(ctx, saleOrderID)
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}

_, _ = writer.Write([]byte(fmt.Sprintf("SaleOrder ID = %d", saleOrder.ID)))

return
}

func (h *Handler) checkAccess(request *http.Request) error {
if request.Method == http.MethodDelete {
return errors.New("access denied") // demo only
}
return nil
}

func (h *Handler) validateAndPrepare(request *http.Request) (uint64, error) {
id, err := strconv.ParseInt(request.URL.Query().Get("id"), 10, 64)
if err != nil {
return 0, fmt.Errorf("bad id: %w", err)
}
if id <= 0 {
return 0, errors.New("bad id")
}

return uint64(id), nil
}
Loading

0 comments on commit 21ccaa9

Please sign in to comment.