diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/go-hexagonal_http_api-course.iml b/.idea/go-hexagonal_http_api-course.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/go-hexagonal_http_api-course.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d247213 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/04-03-command-bus/cmd/api/bootstrap/boostrap.go b/04-03-command-bus/cmd/api/bootstrap/boostrap.go index ae12dad..5c11596 100644 --- a/04-03-command-bus/cmd/api/bootstrap/boostrap.go +++ b/04-03-command-bus/cmd/api/bootstrap/boostrap.go @@ -3,6 +3,7 @@ package bootstrap import ( "database/sql" "fmt" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/fetching" "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/creating" "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/platform/bus/inmemory" @@ -30,16 +31,20 @@ func Run() error { } var ( - commandBus = inmemory.NewCommandBus() + bus = inmemory.NewCommandBus() ) courseRepository := mysql.NewCourseRepository(db) creatingCourseService := creating.NewCourseService(courseRepository) + fetchingCourseService := fetching.NewCourseFetchingService(courseRepository) createCourseCommandHandler := creating.NewCourseCommandHandler(creatingCourseService) - commandBus.Register(creating.CourseCommandType, createCourseCommandHandler) + fetchingCourseQueryHandler := fetching.NewCourseQueryHandler(fetchingCourseService) - srv := server.New(host, port, commandBus) + bus.RegisterCommandHandler(creating.CourseCommandType, createCourseCommandHandler) + bus.RegisterQueryHandler(fetching.CourseQueryType, fetchingCourseQueryHandler) + + srv := server.New(host, port, bus) return srv.Run() } diff --git a/04-03-command-bus/internal/course.go b/04-03-command-bus/internal/course.go index 2e98d82..cc8b397 100644 --- a/04-03-command-bus/internal/course.go +++ b/04-03-command-bus/internal/course.go @@ -87,6 +87,7 @@ type Course struct { // CourseRepository defines the expected behaviour from a course storage. type CourseRepository interface { Save(ctx context.Context, course Course) error + GetAll(ctx context.Context) ([]Course, error) } //go:generate mockery --case=snake --outpkg=storagemocks --output=platform/storage/storagemocks --name=CourseRepository diff --git a/04-03-command-bus/internal/creating/command.go b/04-03-command-bus/internal/creating/command.go index 0c16431..0b7cd76 100644 --- a/04-03-command-bus/internal/creating/command.go +++ b/04-03-command-bus/internal/creating/command.go @@ -4,12 +4,12 @@ import ( "context" "errors" - "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" ) -const CourseCommandType command.Type = "command.creating.course" +const CourseCommandType bus.Type = "bus.creating.course" -// CourseCommand is the command dispatched to create a new course. +// CourseCommand is the bus dispatched to create a new course. type CourseCommand struct { id string name string @@ -25,11 +25,11 @@ func NewCourseCommand(id, name, duration string) CourseCommand { } } -func (c CourseCommand) Type() command.Type { +func (c CourseCommand) Type() bus.Type { return CourseCommandType } -// CourseCommandHandler is the command handler +// CourseCommandHandler is the bus handler // responsible for creating courses. type CourseCommandHandler struct { service CourseService @@ -42,11 +42,11 @@ func NewCourseCommandHandler(service CourseService) CourseCommandHandler { } } -// Handle implements the command.Handler interface. -func (h CourseCommandHandler) Handle(ctx context.Context, cmd command.Command) error { +// Handle implements the bus.CommandHandler interface. +func (h CourseCommandHandler) Handle(ctx context.Context, cmd bus.Command) error { createCourseCmd, ok := cmd.(CourseCommand) if !ok { - return errors.New("unexpected command") + return errors.New("unexpected bus") } return h.service.CreateCourse( diff --git a/04-03-command-bus/internal/creating/service.go b/04-03-command-bus/internal/creating/service.go index 60a7b35..4ae999b 100644 --- a/04-03-command-bus/internal/creating/service.go +++ b/04-03-command-bus/internal/creating/service.go @@ -27,3 +27,4 @@ func (s CourseService) CreateCourse(ctx context.Context, id, name, duration stri } return s.courseRepository.Save(ctx, course) } + diff --git a/04-03-command-bus/internal/fetching/query.go b/04-03-command-bus/internal/fetching/query.go new file mode 100644 index 0000000..d20845f --- /dev/null +++ b/04-03-command-bus/internal/fetching/query.go @@ -0,0 +1,45 @@ +package fetching + +import ( + "context" + "errors" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" +) + +const CourseQueryType bus.Type = "bus.fetching.courses" + +// CourseQuery is the bus dispatched to create a new course. +type CourseQuery struct { +} + +// NewFetchCourseQuery creates a new CourseQuery. +func NewFetchCourseQuery() CourseQuery { + return CourseQuery{} +} + +func (c CourseQuery) Type() bus.Type { + return CourseQueryType +} + +// CourseQueryHandler is the bus handler +// responsible for fetching courses. +type CourseQueryHandler struct { + service FetchingCourseService +} + +// NewCourseQueryHandler initializes a new NewCourseQueryHandler. +func NewCourseQueryHandler(service FetchingCourseService) CourseQueryHandler { + return CourseQueryHandler{ + service: service, + } +} + +// Handle implements the bus.QueryHandler interface. +func (h CourseQueryHandler) Handle(ctx context.Context, query bus.Query) (bus.QueryResponse, error) { + _, ok := query.(CourseQuery) + if !ok { + return nil, errors.New("unexpected bus") + } + + return h.service.GetAll(ctx) +} diff --git a/04-03-command-bus/internal/fetching/service.go b/04-03-command-bus/internal/fetching/service.go new file mode 100644 index 0000000..39ea302 --- /dev/null +++ b/04-03-command-bus/internal/fetching/service.go @@ -0,0 +1,25 @@ +package fetching + +import ( + "context" + + mooc "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal" +) + +// CourseService is the default CourseService interface +// implementation returned by fetching.NewCourseFetchingService. +type FetchingCourseService struct { + courseRepository mooc.CourseRepository +} + +// NewCourseService returns the default Service interface implementation. +func NewCourseFetchingService(courseRepository mooc.CourseRepository) FetchingCourseService { + return FetchingCourseService{ + courseRepository: courseRepository, + } +} + +// CreateCourse implements the creating.CourseService interface. +func (s FetchingCourseService) GetAll(ctx context.Context) ([]mooc.Course, error) { + return s.courseRepository.GetAll(ctx) +} diff --git a/04-03-command-bus/internal/platform/bus/inmemory/bus.go b/04-03-command-bus/internal/platform/bus/inmemory/bus.go new file mode 100644 index 0000000..5ffa852 --- /dev/null +++ b/04-03-command-bus/internal/platform/bus/inmemory/bus.go @@ -0,0 +1,86 @@ +package inmemory + +import ( + "context" + "log" + + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" +) + +// Bus is an in-memory implementation of the bus.Bus. +type Bus struct { + commandsHandlers map[bus.Type]bus.CommandHandler + queryHandlers map[bus.Type]bus.QueryHandler + eventHandlers map[bus.Type]bus.EventHandler +} + +// NewCommandBus initializes a new instance of Bus. +func NewCommandBus() *Bus { + return &Bus{ + commandsHandlers: make(map[bus.Type]bus.CommandHandler), + queryHandlers: make(map[bus.Type]bus.QueryHandler), + eventHandlers: make(map[bus.Type]bus.EventHandler), + } +} + +// DispatchCommand implements the bus.Bus interface. +func (b *Bus) DispatchCommand(ctx context.Context, cmd bus.Command) error { + handler, ok := b.commandsHandlers[cmd.Type()] + if !ok { + return nil + } + + // Si dejo este asincronismo sucede que el endpoint del controller responde 201, cuando en verdad fallo. No tiene más sentido que el asincrnismo lo maneje el que lo llama? + /*go func() { + err := handler.Handle(ctx, cmd) + if err != nil { + log.Printf("Error while handling %s - %s\n", cmd.Type(), err) + } + + }()*/ + + return handler.Handle(ctx, cmd) +} + +// RegisterCommandHandler implements the bus.Bus interface. +func (b *Bus) RegisterCommandHandler(cmdType bus.Type, handler bus.CommandHandler) { + b.commandsHandlers[cmdType] = handler +} + +// DispatchQuery implements the bus.Bus interface. +func (b *Bus) DispatchQuery(ctx context.Context, query bus.Query) (bus.QueryResponse, error) { + handler, ok := b.queryHandlers[query.Type()] + if !ok { + return nil, nil + } + // Como este es sincronico, decide el que lo usa si lo espera o no. + return handler.Handle(ctx, query) +} + +// RegisterQueryHandler implements the bus.Bus interface. +func (b *Bus) RegisterQueryHandler(queryType bus.Type, handler bus.QueryHandler) { + b.queryHandlers[queryType] = handler +} + +// DispatchEvent implements the bus.Bus interface. +func (b *Bus) DispatchEvent(ctx context.Context, event bus.Event) error { + handler, ok := b.eventHandlers[event.Type()] + if !ok { + return nil + } + + go func() { + err := handler.Handle(ctx, event) + if err != nil { + log.Printf("Error while handling %s - %s\n", event.Type(), err) + } + + }() + + return nil +} + +// RegisterEventHandler implements the bus.Bus interface. +func (b *Bus) RegisterEventHandler(cmdType bus.Type, handler bus.EventHandler) { + b.eventHandlers[cmdType] = handler +} \ No newline at end of file diff --git a/04-03-command-bus/internal/platform/bus/inmemory/command.go b/04-03-command-bus/internal/platform/bus/inmemory/command.go deleted file mode 100644 index 08c4d19..0000000 --- a/04-03-command-bus/internal/platform/bus/inmemory/command.go +++ /dev/null @@ -1,43 +0,0 @@ -package inmemory - -import ( - "context" - "log" - - "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command" -) - -// CommandBus is an in-memory implementation of the command.Bus. -type CommandBus struct { - handlers map[command.Type]command.Handler -} - -// NewCommandBus initializes a new instance of CommandBus. -func NewCommandBus() *CommandBus { - return &CommandBus{ - handlers: make(map[command.Type]command.Handler), - } -} - -// Dispatch implements the command.Bus interface. -func (b *CommandBus) Dispatch(ctx context.Context, cmd command.Command) error { - handler, ok := b.handlers[cmd.Type()] - if !ok { - return nil - } - - go func() { - err := handler.Handle(ctx, cmd) - if err != nil { - log.Printf("Error while handling %s - %s\n", cmd.Type(), err) - } - - }() - - return nil -} - -// Register implements the command.Bus interface. -func (b *CommandBus) Register(cmdType command.Type, handler command.Handler) { - b.handlers[cmdType] = handler -} diff --git a/04-03-command-bus/internal/platform/server/handler/courses/create.go b/04-03-command-bus/internal/platform/server/handler/courses/create.go index c178a7f..c62bcda 100644 --- a/04-03-command-bus/internal/platform/server/handler/courses/create.go +++ b/04-03-command-bus/internal/platform/server/handler/courses/create.go @@ -6,7 +6,7 @@ import ( mooc "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal" "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/creating" - "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" "github.com/gin-gonic/gin" ) @@ -17,7 +17,7 @@ type createRequest struct { } // CreateHandler returns an HTTP handler for courses creation. -func CreateHandler(commandBus command.Bus) gin.HandlerFunc { +func CreateHandler(bus bus.Bus) gin.HandlerFunc { return func(ctx *gin.Context) { var req createRequest if err := ctx.BindJSON(&req); err != nil { @@ -25,7 +25,7 @@ func CreateHandler(commandBus command.Bus) gin.HandlerFunc { return } - err := commandBus.Dispatch(ctx, creating.NewCourseCommand( + err := bus.DispatchCommand(ctx, creating.NewCourseCommand( req.ID, req.Name, req.Duration, diff --git a/04-03-command-bus/internal/platform/server/handler/courses/create_test.go b/04-03-command-bus/internal/platform/server/handler/courses/create_test.go index 172c689..7bb5c4d 100644 --- a/04-03-command-bus/internal/platform/server/handler/courses/create_test.go +++ b/04-03-command-bus/internal/platform/server/handler/courses/create_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "testing" - "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command/commandmocks" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus/busmocks" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -15,16 +15,16 @@ import ( ) func TestHandler_Create(t *testing.T) { - commandBus := new(commandmocks.Bus) - commandBus.On( - "Dispatch", + bus := new(busmocks.Bus) + bus.On( + "DispatchCommand", mock.Anything, mock.AnythingOfType("creating.CourseCommand"), ).Return(nil) gin.SetMode(gin.TestMode) r := gin.New() - r.POST("/courses", CreateHandler(commandBus)) + r.POST("/courses", CreateHandler(bus)) t.Run("given an invalid request it returns 400", func(t *testing.T) { createCourseReq := createRequest{ diff --git a/04-03-command-bus/internal/platform/server/handler/courses/get.go b/04-03-command-bus/internal/platform/server/handler/courses/get.go new file mode 100644 index 0000000..07307c2 --- /dev/null +++ b/04-03-command-bus/internal/platform/server/handler/courses/get.go @@ -0,0 +1,41 @@ +package courses + +import ( + mooc "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/fetching" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" + "github.com/gin-gonic/gin" + "net/http" +) + +type getResponse struct { + Id string `json:"id"` + Name string `json:"name"` + Duration string `json:"duration"` +} + +// GetHandler returns an HTTP handler for courses. +func GetHandler(queryBus bus.Bus) gin.HandlerFunc { + return func(ctx *gin.Context) { + var queryResponse, err = queryBus.DispatchQuery(ctx, fetching.NewFetchCourseQuery()) + + if err != nil { + // Si quiero devolver error en ves de la lista se rompe me genera un error de unmarshal + ctx.JSON(http.StatusInternalServerError, []getResponse{}) + return + } + courses, ok := queryResponse.([]mooc.Course) + if ok { + var response = make([]getResponse, 0, len(courses)) + for _, course := range courses { + response = append(response, getResponse{ + Id: course.ID().String(), + Name: course.Name().String(), + Duration: course.Duration().String(), + }) + } + ctx.JSON(http.StatusOK, response) + } + ctx.JSON(http.StatusInternalServerError, []getResponse{}) + } +} diff --git a/04-03-command-bus/internal/platform/server/handler/courses/get_test.go b/04-03-command-bus/internal/platform/server/handler/courses/get_test.go new file mode 100644 index 0000000..4884291 --- /dev/null +++ b/04-03-command-bus/internal/platform/server/handler/courses/get_test.go @@ -0,0 +1,85 @@ +package courses + +import ( + "encoding/json" + "errors" + mooc "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/fetching" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/platform/bus/inmemory" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/platform/storage/storagemocks" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "log" + "net/http" + "net/http/httptest" + "testing" +) + +func TestGetHandler(t *testing.T) { + tests := map[string]struct { + mockData []mooc.Course + mockError error + expectedResponse []getResponse + expectedStatus int + }{ + "Empty repo return 200 with empty list": { + mockData: []mooc.Course{}, + mockError: nil, + expectedStatus:http.StatusOK, + expectedResponse: []getResponse{}}, + "Repo error return 500": { + mockData: []mooc.Course{}, + mockError: errors.New("the field Duration can not be empty"), + expectedStatus:http.StatusInternalServerError, + expectedResponse: []getResponse{}}, + "Fully repo return 200 with list of courses":{ + mockData: []mooc.Course{ mockCourse("8a1c5cdc-ba57-445a-994d-aa412d23723f", "Courses Complete", "123")}, + mockError: nil, + expectedStatus:http.StatusOK, + expectedResponse: []getResponse{{Id: "8a1c5cdc-ba57-445a-994d-aa412d23723f", Name: "Courses Complete", Duration: "123"}}}, + } + for key, value := range tests { + t.Run(key, func(t *testing.T) { + courseRepository := storagemocks.CourseRepository{} + bus := inmemory.NewCommandBus() + fetchingCourseService := fetching.NewCourseFetchingService(&courseRepository) + fetchingCourseQueryHandler := fetching.NewCourseQueryHandler(fetchingCourseService) + bus.RegisterQueryHandler(fetching.CourseQueryType, fetchingCourseQueryHandler) + + courseRepository.On( + "GetAll", + mock.Anything, + ).Return(value.mockData, value.mockError) + gin.SetMode(gin.TestMode) + r := gin.New() + r.GET("/courses", GetHandler(bus)) + + req, err := http.NewRequest(http.MethodGet, "/courses", nil) + require.NoError(t, err) + + rec := httptest.NewRecorder() + r.ServeHTTP(rec, req) + + res := rec.Result() + defer res.Body.Close() + + assert.Equal(t, value.expectedStatus, res.StatusCode) + var response []getResponse + if err := json.NewDecoder(res.Body).Decode(&response); err != nil { + log.Fatalln(err) + } + + assert.Equal(t, value.expectedResponse, response) + }) + } +} + +func mockCourse(id string, name string, duration string) mooc.Course { + course, err := mooc.NewCourse(id, name, duration) + if err != nil{ + log.Fatalln(err) + } + return course +} diff --git a/04-03-command-bus/internal/platform/server/server.go b/04-03-command-bus/internal/platform/server/server.go index 09c4d34..993ebb7 100644 --- a/04-03-command-bus/internal/platform/server/server.go +++ b/04-03-command-bus/internal/platform/server/server.go @@ -6,7 +6,7 @@ import ( "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/platform/server/handler/courses" "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/internal/platform/server/handler/health" - "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command" + "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" "github.com/gin-gonic/gin" ) @@ -15,15 +15,15 @@ type Server struct { engine *gin.Engine // deps - commandBus command.Bus + bus bus.Bus } -func New(host string, port uint, commandBus command.Bus) Server { +func New(host string, port uint, bus bus.Bus) Server { srv := Server{ engine: gin.New(), httpAddr: fmt.Sprintf("%s:%d", host, port), - commandBus: commandBus, + bus: bus, } srv.registerRoutes() @@ -37,5 +37,6 @@ func (s *Server) Run() error { func (s *Server) registerRoutes() { s.engine.GET("/health", health.CheckHandler()) - s.engine.POST("/courses", courses.CreateHandler(s.commandBus)) + s.engine.POST("/courses", courses.CreateHandler(s.bus)) + s.engine.GET("/courses", courses.GetHandler(s.bus)) } diff --git a/04-03-command-bus/internal/platform/storage/mysql/course_repository.go b/04-03-command-bus/internal/platform/storage/mysql/course_repository.go index be308b4..08186ce 100644 --- a/04-03-command-bus/internal/platform/storage/mysql/course_repository.go +++ b/04-03-command-bus/internal/platform/storage/mysql/course_repository.go @@ -37,3 +37,34 @@ func (r *CourseRepository) Save(ctx context.Context, course mooc.Course) error { return nil } + + +// GetAll implements the mooc.CourseRepository interface. +func (r *CourseRepository) GetAll(ctx context.Context) (courses []mooc.Course, err error) { + courseSQLStruct := sqlbuilder.NewSelectBuilder() + courseSQLStruct.Select("id", "name", "duration") + courseSQLStruct.From(sqlCourseTable) + + sqlQuery, args := courseSQLStruct.Build() + + + rows, err := r.db.QueryContext(ctx, sqlQuery, args...) + if err != nil { + return nil, fmt.Errorf("error trying to get course on database: %v", err) + } + defer rows.Close() + courses = []mooc.Course{} + for rows.Next() { + var sqlCourse sqlCourse + err := rows.Scan(sqlCourse.ID, sqlCourse.Name, sqlCourse.Duration) + if err != nil { + return nil, err + } + course, err := mooc.NewCourse(sqlCourse.ID, sqlCourse.Name, sqlCourse.Duration) + if err != nil { + return nil, err + } + courses = append(courses, course) + } + return courses, nil +} \ No newline at end of file diff --git a/04-03-command-bus/internal/platform/storage/storagemocks/course_repository.go b/04-03-command-bus/internal/platform/storage/storagemocks/course_repository.go index 146a323..6b364cd 100644 --- a/04-03-command-bus/internal/platform/storage/storagemocks/course_repository.go +++ b/04-03-command-bus/internal/platform/storage/storagemocks/course_repository.go @@ -14,6 +14,29 @@ type CourseRepository struct { mock.Mock } +// GetAll provides a mock function with given fields: ctx +func (_m *CourseRepository) GetAll(ctx context.Context) ([]mooc.Course, error) { + ret := _m.Called(ctx) + + var r0 []mooc.Course + if rf, ok := ret.Get(0).(func(context.Context) []mooc.Course); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]mooc.Course) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Save provides a mock function with given fields: ctx, course func (_m *CourseRepository) Save(ctx context.Context, course mooc.Course) error { ret := _m.Called(ctx, course) diff --git a/04-03-command-bus/kit/bus/bus.go b/04-03-command-bus/kit/bus/bus.go new file mode 100644 index 0000000..f978c79 --- /dev/null +++ b/04-03-command-bus/kit/bus/bus.go @@ -0,0 +1,57 @@ +package bus + +import "context" + +// Bus defines the expected behaviour from a bus bus. +type Bus interface { + // DispatchCommand is the method used to dispatch new commands. + DispatchCommand(context.Context, Command) error + // RegisterCommandHandler is the method used to register a new command handler. + RegisterCommandHandler(Type, CommandHandler) + // DispatchQuery is the method used to dispatch new queries. + DispatchQuery(context.Context, Query) (QueryResponse, error) + // RegisterQueryHandler is the method used to register a new query handler. + RegisterQueryHandler(Type, QueryHandler) + // DispatchEvent is the method used to dispatch new event. + DispatchEvent(context.Context, Event) error + // RegisterEventHandler is the method used to register a new event handler. + RegisterEventHandler(Type, EventHandler) +} + +//go:generate mockery --case=snake --outpkg=busmocks --output=busmocks --name=Bus + +// Type represents an application bus type. +type Type string + +// Command represents an application bus. +type Command interface { + Type() Type +} + +// CommandHandler defines the expected behaviour from a bus handler. +type CommandHandler interface { + Handle(context.Context, Command) error +} + +// Query represents an application bus. +type Query interface { + Type() Type +} + +type QueryResponse interface{} + +// QueryHandler defines the expected behaviour from a bus handler. +type QueryHandler interface { + Handle(context.Context, Query) (QueryResponse, error) +} + + +// Event represents an application bus. +type Event interface { + Type() Type +} + +// EventHandler defines the expected behaviour from a bus handler. +type EventHandler interface { + Handle(context.Context, Event) error +} diff --git a/04-03-command-bus/kit/bus/busmocks/bus.go b/04-03-command-bus/kit/bus/busmocks/bus.go new file mode 100644 index 0000000..fbdd0c5 --- /dev/null +++ b/04-03-command-bus/kit/bus/busmocks/bus.go @@ -0,0 +1,82 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package busmocks + +import ( + context "context" + + bus "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/bus" + + mock "github.com/stretchr/testify/mock" +) + +// Bus is an autogenerated mock type for the Bus type +type Bus struct { + mock.Mock +} + +// DispatchCommand provides a mock function with given fields: _a0, _a1 +func (_m *Bus) DispatchCommand(_a0 context.Context, _a1 bus.Command) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, bus.Command) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DispatchEvent provides a mock function with given fields: _a0, _a1 +func (_m *Bus) DispatchEvent(_a0 context.Context, _a1 bus.Event) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, bus.Event) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DispatchQuery provides a mock function with given fields: _a0, _a1 +func (_m *Bus) DispatchQuery(_a0 context.Context, _a1 bus.Query) (bus.QueryResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 bus.QueryResponse + if rf, ok := ret.Get(0).(func(context.Context, bus.Query) bus.QueryResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(bus.QueryResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bus.Query) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RegisterCommandHandler provides a mock function with given fields: _a0, _a1 +func (_m *Bus) RegisterCommandHandler(_a0 bus.Type, _a1 bus.CommandHandler) { + _m.Called(_a0, _a1) +} + +// RegisterEventHandler provides a mock function with given fields: _a0, _a1 +func (_m *Bus) RegisterEventHandler(_a0 bus.Type, _a1 bus.EventHandler) { + _m.Called(_a0, _a1) +} + +// RegisterQueryHandler provides a mock function with given fields: _a0, _a1 +func (_m *Bus) RegisterQueryHandler(_a0 bus.Type, _a1 bus.QueryHandler) { + _m.Called(_a0, _a1) +} diff --git a/04-03-command-bus/kit/command/command.go b/04-03-command-bus/kit/command/command.go deleted file mode 100644 index a7f36bc..0000000 --- a/04-03-command-bus/kit/command/command.go +++ /dev/null @@ -1,26 +0,0 @@ -package command - -import "context" - -// Bus defines the expected behaviour from a command bus. -type Bus interface { - // Dispatch is the method used to dispatch new commands. - Dispatch(context.Context, Command) error - // Register is the method used to register a new command handler. - Register(Type, Handler) -} - -//go:generate mockery --case=snake --outpkg=commandmocks --output=commandmocks --name=Bus - -// Type represents an application command type. -type Type string - -// Command represents an application command. -type Command interface { - Type() Type -} - -// Handler defines the expected behaviour from a command handler. -type Handler interface { - Handle(context.Context, Command) error -} diff --git a/04-03-command-bus/kit/command/commandmocks/bus.go b/04-03-command-bus/kit/command/commandmocks/bus.go deleted file mode 100644 index 347ea4e..0000000 --- a/04-03-command-bus/kit/command/commandmocks/bus.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by mockery v0.0.0-dev. DO NOT EDIT. - -package commandmocks - -import ( - context "context" - - command "github.com/CodelyTV/go-hexagonal_http_api-course/04-03-command-bus/kit/command" - - mock "github.com/stretchr/testify/mock" -) - -// Bus is an autogenerated mock type for the Bus type -type Bus struct { - mock.Mock -} - -// Dispatch provides a mock function with given fields: _a0, _a1 -func (_m *Bus) Dispatch(_a0 context.Context, _a1 command.Command) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, command.Command) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Register provides a mock function with given fields: _a0, _a1 -func (_m *Bus) Register(_a0 command.Type, _a1 command.Handler) { - _m.Called(_a0, _a1) -}