Skip to content

Commit 0aca3aa

Browse files
committed
feat: abstract out transport layer
1 parent abe6f67 commit 0aca3aa

7 files changed

Lines changed: 219 additions & 126 deletions

File tree

example

7.9 MB
Binary file not shown.

internal/errors/errors.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package errors
2+
3+
import "errors"
4+
5+
var (
6+
ErrNotFound = errors.New("not found")
7+
ErrBadRequest = errors.New("bad request")
8+
)

internal/transport/http.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package transport
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
8+
appErr "example/internal/errors"
9+
"example/internal/users"
10+
11+
"github.com/labstack/echo/v4"
12+
)
13+
14+
type HTTPConfig struct {
15+
Port int
16+
UsersService users.Service
17+
}
18+
19+
type HTTPServer struct {
20+
e *echo.Echo
21+
port int
22+
}
23+
24+
func NewHTTPServer(config *HTTPConfig) *HTTPServer {
25+
e := echo.New()
26+
registerHTTPRoutes(e, config.UsersService)
27+
return &HTTPServer{
28+
e: e,
29+
port: config.Port,
30+
}
31+
}
32+
33+
func (server *HTTPServer) Start() {
34+
server.e.Logger.Fatal(server.e.Start(fmt.Sprintf(":%d", server.port)))
35+
}
36+
37+
func registerHTTPRoutes(e *echo.Echo, usersService users.Service) {
38+
e.PUT("/users/:id", updateUser(usersService))
39+
}
40+
41+
type updateUserRequest struct {
42+
ID string `param:"id"`
43+
FirstName string `json:"first_name"`
44+
LastName string `json:"last_name"`
45+
}
46+
47+
type updateUserResponse struct {
48+
ID string `json:"id"`
49+
FirstName string `json:"first_name"`
50+
LastName string `json:"last_name"`
51+
}
52+
53+
func updateUser(usersService users.Service) echo.HandlerFunc {
54+
return func(c echo.Context) error {
55+
var request updateUserRequest
56+
err := c.Bind(&request)
57+
if err != nil {
58+
return c.NoContent(http.StatusBadRequest)
59+
}
60+
61+
resp, err := usersService.UpdateUser(users.UpdateUserRequest{
62+
ID: request.ID,
63+
FirstName: request.FirstName,
64+
LastName: request.LastName,
65+
})
66+
67+
if err != nil {
68+
return c.NoContent(MapErrorToHTTPStatus(err))
69+
}
70+
return c.JSON(http.StatusOK, updateUserResponse{
71+
ID: resp.ID,
72+
FirstName: resp.FirstName,
73+
LastName: resp.LastName,
74+
})
75+
}
76+
}
77+
78+
func MapErrorToHTTPStatus(err error) int {
79+
switch {
80+
case errors.Is(err, appErr.ErrNotFound):
81+
return http.StatusNotFound
82+
case errors.Is(err, appErr.ErrBadRequest):
83+
return http.StatusNotFound
84+
}
85+
return http.StatusInternalServerError
86+
}

internal/users/service.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package users
2+
3+
import (
4+
appErr "example/internal/errors"
5+
"fmt"
6+
)
7+
8+
type UpdateUserRequest struct {
9+
ID string
10+
FirstName string
11+
LastName string
12+
}
13+
14+
type UpdateUserResponse struct {
15+
ID string
16+
FirstName string
17+
LastName string
18+
}
19+
20+
type User struct {
21+
ID string
22+
FirstName string
23+
LastName string
24+
}
25+
26+
type Service struct {
27+
DB map[string]*User
28+
}
29+
30+
func (s *Service) UpdateUser(request UpdateUserRequest) (UpdateUserResponse, error) {
31+
user, ok := s.DB[request.ID]
32+
if !ok {
33+
return UpdateUserResponse{}, fmt.Errorf("unable to find user in db: %w", appErr.ErrNotFound)
34+
}
35+
36+
if request.FirstName != "" {
37+
user.FirstName = request.FirstName
38+
}
39+
40+
if request.LastName != "" {
41+
user.LastName = request.LastName
42+
}
43+
44+
return UpdateUserResponse{
45+
ID: user.ID,
46+
FirstName: user.FirstName,
47+
LastName: user.LastName,
48+
}, nil
49+
}

internal/users/service_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package users_test
2+
3+
import (
4+
"testing"
5+
6+
appErr "example/internal/errors"
7+
"example/internal/users"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestUpdateUser(t *testing.T) {
13+
14+
t.Run("should update user when user exists", func(t *testing.T) {
15+
16+
mockDB := map[string]*users.User{
17+
"test_user_id": &users.User{
18+
ID: "test_user_id",
19+
FirstName: "Test",
20+
LastName: "User",
21+
},
22+
}
23+
24+
request := users.UpdateUserRequest{
25+
ID: "test_user_id",
26+
FirstName: "UpdatedTest",
27+
LastName: "UpdatedUser",
28+
}
29+
expectedResponse := users.UpdateUserResponse{
30+
ID: "test_user_id",
31+
FirstName: "UpdatedTest",
32+
LastName: "UpdatedUser",
33+
}
34+
s := users.Service{DB: mockDB}
35+
36+
if response, err := s.UpdateUser(request); assert.NoError(t, err) {
37+
user := mockDB["test_user_id"]
38+
assert.Equal(t, expectedResponse, response)
39+
assert.Equal(t, "UpdatedTest", user.FirstName)
40+
assert.Equal(t, "UpdatedUser", user.LastName)
41+
}
42+
})
43+
44+
t.Run("should return ErrNotFound when user not found", func(t *testing.T) {
45+
mockDB := map[string]*users.User{
46+
"test_user_id": &users.User{
47+
ID: "test_user_id",
48+
FirstName: "Test",
49+
LastName: "User",
50+
},
51+
}
52+
53+
request := users.UpdateUserRequest{
54+
ID: "bad_user_id",
55+
FirstName: "UpdatedTest",
56+
LastName: "UpdatedUser",
57+
}
58+
59+
s := users.Service{DB: mockDB}
60+
61+
if response, err := s.UpdateUser(request); assert.ErrorIs(t, err, appErr.ErrNotFound) {
62+
assert.Equal(t, users.UpdateUserResponse{}, response)
63+
}
64+
})
65+
}

main.go

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package main
22

33
import (
4-
"net/http"
5-
6-
"github.com/labstack/echo/v4"
4+
"example/internal/transport"
5+
"example/internal/users"
76
)
87

98
type User struct {
@@ -13,27 +12,15 @@ type User struct {
1312
}
1413

1514
func main() {
16-
userDB := make(map[string]*User)
17-
e := echo.New()
18-
e.PUT("/users/:id", updateUser(userDB))
19-
e.Logger.Fatal(e.Start(":8080"))
20-
}
21-
22-
func updateUser(userDB map[string]*User) echo.HandlerFunc {
23-
return func(c echo.Context) error {
24-
var user User
25-
err := c.Bind(&user)
26-
if err != nil {
27-
return c.NoContent(http.StatusBadRequest)
28-
}
29-
dbUser, ok := userDB[user.ID]
30-
if !ok {
31-
return c.NoContent(http.StatusNotFound)
32-
}
15+
userDB := make(map[string]*users.User)
16+
usersService := users.Service{
17+
DB: userDB,
18+
}
3319

34-
dbUser.FirstName = user.FirstName
35-
dbUser.LastName = user.LastName
20+
httpService := transport.NewHTTPServer(&transport.HTTPConfig{
21+
Port: 8080,
22+
UsersService: usersService,
23+
})
3624

37-
return c.JSON(http.StatusOK, dbUser)
38-
}
25+
httpService.Start()
3926
}

main_test.go

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)