Skip to content

Commit

Permalink
feat: add function handler
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristophBe committed Sep 14, 2021
1 parent 451ed88 commit 19c3b65
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 6 deletions.
33 changes: 33 additions & 0 deletions handlers/function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package handlers

import (
"github.com/ChristophBe/go-crud/types"
"net/http"
)

// NewFunctionHandler creates a http.Handler that handles the creation of a model
func NewFunctionHandler(service types.FunctionHandlerService, responseWriter types.ResponseWriter, errorWriter types.ErrorResponseWriter) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
ctx := request.Context()
dto, err := service.ParseValidatableFromRequest(request)
if err != nil {
errorWriter(err, writer, request)
return
}

if err = dto.IsValid(ctx, false); err != nil {
errorWriter(err, writer, request)
return
}

result, status, err := service.Function(ctx, dto)
if err != nil {
errorWriter(err, writer, request)
return
}

if err = responseWriter(result, status, writer, request); err != nil {
errorWriter(err, writer, request)
}
}
}
105 changes: 105 additions & 0 deletions handlers/function_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package handlers

import (
"errors"
"net/http"
"net/http/httptest"
"testing"
)

func TestCrudHandlersImpl_Function(t *testing.T) {

tt := []struct {
name string
service functionServiceMock
responseWriterError error
expectedError error
}{
{
name: "dto parsing failed",
service: functionServiceMock{
dtoErr: errors.New("test"),
},
expectedError: errors.New("test"),
},
{
name: "dto invalid",
service: functionServiceMock{
dto: dtoMock{
validationError: errors.New("test"),
},
},
expectedError: errors.New("test"),
},
{
name: "function returns error invalid",
service: functionServiceMock{
dto: dtoMock{},
functionErr: errors.New("test"),
},
expectedError: errors.New("test"),
},
{
name: "function succeeded invalid",
service: functionServiceMock{
dto: dtoMock{},
responseStatus: http.StatusOK,
},
expectedError: nil,
},
{
name: "response writer returns error",
service: functionServiceMock{
dto: dtoMock{},
responseStatus: http.StatusOK,
},
responseWriterError: errors.New("test-error"),
expectedError: errors.New("test-error"),
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {

responseRecorder := new(responseWriterRecorder)
errorRecorder := new(errorWriterRecorder)

responseWriter := newMockResponseWriter(responseRecorder, tc.responseWriterError)

errorWriter := newMockErrorWriter(errorRecorder)

handler := NewFunctionHandler(tc.service, responseWriter, errorWriter)
w := httptest.ResponseRecorder{}
handler.ServeHTTP(&w, new(http.Request))

if tc.expectedError != nil {

// expect error writer to be called
if errorRecorder.err == nil {
t.Error("error to be not nil")
return
}
if errorRecorder.err.Error() != tc.expectedError.Error() {
t.Errorf("expected err to be %v, got %v", tc.expectedError, errorRecorder.err)
}
return
}
if tc.expectedError == nil {
// expect response writer to be called

if responseRecorder.status != tc.service.responseStatus {
t.Errorf("expected response status to be %v, got %v", tc.service.responseStatus, responseRecorder.status)
}
if responseRecorder.body != nil {
t.Errorf("expected response body to be nil, got %v", responseRecorder.body)
}

} else {
// expect response not to called
if responseRecorder.called {
t.Error("expected response writer not to be called")
}
}
})
}
}
15 changes: 15 additions & 0 deletions handlers/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ func (m getAllServiceMock) GetAll(_ *http.Request) ([]types.Model, error) {
return m.models, m.err
}

type functionServiceMock struct {
functionErr error
dtoErr error
result interface{}
responseStatus int
dto types.Validatable
}

func (f functionServiceMock) Function(_ context.Context, _ types.Validatable) (interface{}, int, error) {
return f.result, f.responseStatus, f.functionErr
}
func (f functionServiceMock) ParseValidatableFromRequest(_ *http.Request) (types.Validatable, error) {
return f.dto, f.dtoErr
}

type modelErrorHolder struct {
model types.Model
err error
Expand Down
13 changes: 9 additions & 4 deletions types/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package types

import "context"

// Dto is the type that contains the structure of the data that your api expect to receive.
// It contains a method to validate it self and to convert it to its corresponding model object.
type Dto interface {
// Validatable is an interface that makes sure the typ can be validated.
type Validatable interface {
// IsValid validates the values of the dto.
// If partial is true only values that are not there zero value should be validated. Otherwise validate all values.
// If partial is true only values that are not there zero value should be validated. Otherwise, validate all values.
// It will return an error if the validation fails.
IsValid(ctx context.Context, partial bool) error
}

// Dto is the type that contains the structure of the data that your api expect to receive.
// It contains a method to validate itself and to convert it to its corresponding model object.
type Dto interface {
Validatable

// AssignToModel assigns the value of the dto to a Model.
AssignToModel(ctx context.Context, model Model) (Model, error)
Expand Down
12 changes: 10 additions & 2 deletions types/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,24 @@ type GetAllService interface {

// CreateEmptyModelService defines the CreateEmptyModel function that is used in multiple handlers.
type CreateEmptyModelService interface {
// CreateEmptyModel returns a empty instance of the model
// CreateEmptyModel returns an empty instance of the model
CreateEmptyModel(ctx context.Context) Model
}

// ParseDtoFromRequestService defines the ParseDtoFromRequest function that is used in multiple handlers.
type ParseDtoFromRequestService interface {
// ParseDtoFromRequest creates an dto instance based on a request
// ParseDtoFromRequest creates a dto instance based on a request
ParseDtoFromRequest(request *http.Request) (Dto, error)
}

// FunctionHandlerService defines a service to handle a request by a Function
type FunctionHandlerService interface {
// ParseValidatableFromRequest parses a Validatable for the request
ParseValidatableFromRequest(request *http.Request) (Validatable, error)
// Function a function generates a response based on a Validatable
Function(ctx context.Context, dto Validatable) (interface{}, int, error)
}

// CreateService defines functions that are need for the create model handler
type CreateService interface {
CreateEmptyModelService
Expand Down

0 comments on commit 19c3b65

Please sign in to comment.