Skip to content

Commit 71c720e

Browse files
committed
add naive CRD user routes
1 parent 170111b commit 71c720e

File tree

8 files changed

+248
-18
lines changed

8 files changed

+248
-18
lines changed

cmd/api/main.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"sandbox-go-api-sqlboiler-rest-auth/internal/config"
88
"sandbox-go-api-sqlboiler-rest-auth/internal/routes"
99

10+
"go.uber.org/zap/zapcore"
11+
1012
"go.uber.org/zap"
1113

1214
_ "github.com/lib/pq"
@@ -21,7 +23,11 @@ func main() {
2123
}
2224

2325
// logger
24-
logger, _ := zap.NewDevelopment()
26+
zapConfig := zap.NewDevelopmentConfig()
27+
encConfig := zap.NewDevelopmentEncoderConfig()
28+
encConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
29+
zapConfig.EncoderConfig = encConfig
30+
logger, _ := zapConfig.Build()
2531
defer func() {
2632
_ = logger.Sync()
2733
}()

internal/handlers/_template.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package handlers
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/labstack/echo/v4"
8+
)
9+
10+
func (h *Handlers) GetUsers(c echo.Context) error {
11+
ctx := context.Background()
12+
return errors.New("not implemented")
13+
}
14+
15+
func (h *Handlers) GetUser(c echo.Context) error {
16+
ctx := context.Background()
17+
return errors.New("not implemented")
18+
}
19+
20+
func (h *Handlers) CreateUser(c echo.Context) error {
21+
ctx := context.Background()
22+
return errors.New("not implemented")
23+
}
24+
25+
func (h *Handlers) PatchUser(c echo.Context) error {
26+
ctx := context.Background()
27+
return errors.New("not implemented")
28+
}
29+
30+
func (h *Handlers) DeleteUser(c echo.Context) error {
31+
ctx := context.Background()
32+
return errors.New("not implemented")
33+
}

internal/handlers/handlers.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package handlers
2+
3+
import (
4+
"database/sql"
5+
6+
"go.uber.org/zap"
7+
)
8+
9+
type Handlers struct {
10+
db *sql.DB
11+
logger *zap.Logger
12+
slogger *zap.SugaredLogger
13+
}
14+
15+
func NewHandler(db *sql.DB, l *zap.Logger) *Handlers {
16+
var handler Handlers
17+
handler.db = db
18+
handler.logger = l
19+
handler.slogger = l.Sugar()
20+
return &handler
21+
}

internal/handlers/models.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package handlers
2+
3+
type SuccessResponse struct {
4+
Data interface{} `json:"data"`
5+
}

internal/handlers/status.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ import (
66
"github.com/labstack/echo/v4"
77
)
88

9-
func GetStatus(c echo.Context) error {
9+
func (h *Handlers) GetStatus(c echo.Context) error {
1010
return c.String(http.StatusOK, "server is running")
1111
}

internal/handlers/user.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package handlers
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"sandbox-go-api-sqlboiler-rest-auth/models"
8+
"time"
9+
10+
"github.com/volatiletech/sqlboiler/v4/boil"
11+
12+
"github.com/volatiletech/sqlboiler/v4/queries/qm"
13+
14+
"github.com/labstack/echo/v4"
15+
)
16+
17+
type PublicUser struct {
18+
ID int `boil:"id" json:"id"`
19+
Email string `boil:"email" json:"email"`
20+
CreatedAt time.Time `boil:"created_at" json:"created_at"`
21+
UpdatedAt time.Time `boil:"updated_at" json:"updated_at"`
22+
}
23+
24+
type UsersData struct {
25+
Users *[]PublicUser `json:"users"`
26+
}
27+
28+
func (h *Handlers) GetUsers(c echo.Context) error {
29+
ctx := context.Background()
30+
var users []PublicUser
31+
res := SuccessResponse{
32+
Data: UsersData{Users: &users},
33+
}
34+
err := models.Users().Bind(ctx, h.db, &users)
35+
if err != nil {
36+
c.Error(err)
37+
return err
38+
}
39+
return c.JSON(http.StatusOK, res)
40+
}
41+
42+
type UserData struct {
43+
User *PublicUser `json:"user"`
44+
}
45+
46+
func (h *Handlers) GetUser(c echo.Context) error {
47+
ctx := context.Background()
48+
res := SuccessResponse{
49+
Data: UserData{},
50+
}
51+
var id int
52+
err := echo.PathParamsBinder(c).
53+
Int("id", &id).
54+
BindError()
55+
if err != nil {
56+
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
57+
}
58+
59+
exists, err := models.UserExists(ctx, h.db, id)
60+
if !exists {
61+
return echo.NewHTTPError(http.StatusNotFound, http.StatusText(http.StatusNotFound))
62+
}
63+
if err != nil {
64+
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
65+
}
66+
67+
var users []*PublicUser
68+
err = models.Users(qm.Where("id = ?", id)).Bind(ctx, h.db, &users)
69+
if err != nil {
70+
c.Error(err)
71+
return err
72+
}
73+
res.Data = &users[0]
74+
75+
return c.JSON(http.StatusOK, res)
76+
}
77+
78+
type CreateUserRequest struct {
79+
Email string `json:"email"`
80+
Password string `json:"password"`
81+
}
82+
83+
func (h *Handlers) CreateUser(c echo.Context) error {
84+
ctx := context.Background()
85+
res := SuccessResponse{
86+
Data: UserData{},
87+
}
88+
req := new(CreateUserRequest)
89+
if err := c.Bind(req); err != nil {
90+
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
91+
}
92+
u := models.User{
93+
Email: req.Email,
94+
HashedPassword: req.Password,
95+
}
96+
err := u.Insert(ctx, h.db, boil.Infer())
97+
if err != nil {
98+
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
99+
}
100+
pu := PublicUser{
101+
ID: u.ID,
102+
Email: u.Email,
103+
CreatedAt: u.CreatedAt,
104+
UpdatedAt: u.UpdatedAt,
105+
}
106+
res.Data = UserData{
107+
User: &pu,
108+
}
109+
110+
return c.JSON(http.StatusOK, res)
111+
}
112+
113+
func (h *Handlers) PatchUser(c echo.Context) error {
114+
return errors.New("not implemented")
115+
}
116+
117+
func (h *Handlers) DeleteUser(c echo.Context) error {
118+
ctx := context.Background()
119+
var id int
120+
err := echo.PathParamsBinder(c).
121+
Int("id", &id).
122+
BindError()
123+
if err != nil {
124+
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
125+
}
126+
exists, err := models.UserExists(ctx, h.db, id)
127+
if !exists {
128+
return echo.NewHTTPError(http.StatusNotFound, http.StatusText(http.StatusNotFound))
129+
}
130+
if err != nil {
131+
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
132+
}
133+
u, err := models.FindUser(ctx, h.db, id)
134+
if err != nil {
135+
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
136+
}
137+
138+
_, err = u.Delete(ctx, h.db)
139+
if err != nil {
140+
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
141+
}
142+
return c.NoContent(http.StatusOK)
143+
}

internal/routes/middlewares.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func ZapLogger(log *zap.Logger) echo.MiddlewareFunc {
4444
case n >= 500:
4545
log.With(zap.Error(err)).Error("Server error", fields...)
4646
case n >= 400:
47-
log.With(zap.Error(err)).Warn("Client error", fields...)
47+
log.Info("Client error", fields...)
4848
case n >= 300:
4949
log.Info("Redirection", fields...)
5050
default:

internal/routes/routes.go

+37-15
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@ import (
1414
"github.com/labstack/echo/v4"
1515
)
1616

17-
func NewRouter(db *sql.DB, logger *zap.Logger) *echo.Echo {
17+
func NewRouter(db *sql.DB, l *zap.Logger) *echo.Echo {
18+
h := handlers.NewHandler(db, l)
1819
e := echo.New()
19-
bindRouteMiddlewares(e, logger)
20+
bindRouteMiddlewares(e, l)
21+
2022
// routes
21-
e.GET("/api/status", handlers.GetStatus)
23+
e.GET("/api/status", h.GetStatus)
2224

25+
bindRoutes(e, h)
2326
exportRoutesJson(e)
2427
return e
2528
}
2629

27-
func exportRoutesJson(e *echo.Echo) {
28-
data, err := json.MarshalIndent(e.Routes(), "", " ")
29-
if err != nil {
30-
log.Fatalln(err)
31-
}
32-
err = ioutil.WriteFile("routes.json", data, 0644)
33-
if err != nil {
34-
log.Fatalln(err)
35-
}
30+
func bindRoutes(e *echo.Echo, h *handlers.Handlers) {
31+
e.GET("/api/v1/users", h.GetUsers)
32+
e.POST("/api/v1/users", h.CreateUser)
33+
e.GET("/api/v1/users/:id", h.GetUser)
34+
e.PATCH("/api/v1/users/:id", h.PatchUser)
35+
e.DELETE("/api/v1/users/:id", h.DeleteUser)
3636
}
3737

3838
func bindRouteMiddlewares(e *echo.Echo, logger *zap.Logger) {
@@ -50,7 +50,29 @@ func bindRouteMiddlewares(e *echo.Echo, logger *zap.Logger) {
5050
//}))
5151

5252
// middlewares if dev
53-
//e.Use(middleware.BodyDump(func(c echo.Context, reqBody, resBody []byte) {
54-
// println(reqBody, resBody)
55-
//}))
53+
slogger := logger.Sugar()
54+
e.Use(middleware.BodyDump(func(c echo.Context, reqBody, resBody []byte) {
55+
if len(reqBody) == 0 {
56+
slogger.Debug("request body: ", "None")
57+
} else {
58+
slogger.Debug("request body: ", string(reqBody))
59+
}
60+
61+
if len(resBody) == 0 {
62+
slogger.Debug("response body: ", "No Content")
63+
} else {
64+
slogger.Debug("response body: ", string(resBody))
65+
}
66+
}))
67+
}
68+
69+
func exportRoutesJson(e *echo.Echo) {
70+
data, err := json.MarshalIndent(e.Routes(), "", " ")
71+
if err != nil {
72+
log.Fatalln(err)
73+
}
74+
err = ioutil.WriteFile("routes.json", data, 0644)
75+
if err != nil {
76+
log.Fatalln(err)
77+
}
5678
}

0 commit comments

Comments
 (0)