Skip to content

Commit 19797f9

Browse files
committed
✨ feat: setup basic http router
add default setup
0 parents  commit 19797f9

File tree

12 files changed

+843
-0
lines changed

12 files changed

+843
-0
lines changed

.github/workflows/go.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Go
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
jobs:
11+
build_and_test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Setup go-task
15+
uses: pnorton5432/setup-task@v1
16+
with:
17+
task-version: 3.29.1
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
- name: Setup Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: 'stable'
24+
check-latest: true
25+
- name: Task Build
26+
run: task build
27+
- name: Task Test
28+
run: task test

.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
go.work.sum
23+
24+
# env file
25+
.env
26+
bin

README.md

+269
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# golang-rest-api-sample
2+
3+
This repository is for use golang to implemenation resful api
4+
5+
## setup with server
6+
7+
```golang
8+
package application
9+
10+
import (
11+
"context"
12+
"fmt"
13+
"log"
14+
"net/http"
15+
"time"
16+
17+
"github.com/gin-gonic/gin"
18+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/config"
19+
)
20+
21+
// define app dependency
22+
type App struct {
23+
Router *gin.Engine
24+
config *config.Config
25+
}
26+
27+
func New(config *config.Config) *App {
28+
app := &App{
29+
config: config,
30+
}
31+
app.SetupRoutes()
32+
return app
33+
}
34+
35+
func (app *App) Start(ctx context.Context) error {
36+
server := &http.Server{
37+
Addr: fmt.Sprintf(":%s", app.config.Port),
38+
Handler: app.Router,
39+
}
40+
log.Printf("Starting server on %s", app.config.Port)
41+
errCh := make(chan error, 1)
42+
go func() {
43+
err := server.ListenAndServe()
44+
if err != nil {
45+
errCh <- fmt.Errorf("failed to start server: %w", err)
46+
}
47+
close(errCh)
48+
}()
49+
50+
select {
51+
case err := <-errCh:
52+
return err
53+
case <-ctx.Done():
54+
log.Println("server cancel")
55+
timeout, cancel := context.WithTimeout(context.Background(), time.Second*10)
56+
defer cancel()
57+
return server.Shutdown(timeout)
58+
}
59+
}
60+
```
61+
62+
## setup application
63+
```golang
64+
package main
65+
66+
import (
67+
"context"
68+
"log"
69+
"os"
70+
"os/signal"
71+
"syscall"
72+
73+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/application"
74+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/config"
75+
)
76+
77+
func main() {
78+
app := application.New(config.AppConfig)
79+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
80+
defer cancel()
81+
err := app.Start(ctx)
82+
if err != nil {
83+
log.Fatalf("failed to start app: %v", err)
84+
}
85+
}
86+
```
87+
## setup router
88+
```golang
89+
package news
90+
91+
import (
92+
"net/http"
93+
94+
"github.com/gin-gonic/gin"
95+
)
96+
97+
type Handler struct{}
98+
99+
func NewHandler() *Handler {
100+
return &Handler{}
101+
}
102+
103+
func (h *Handler) RegisterRoute(router *gin.RouterGroup) {
104+
router.POST("", h.CreateNews)
105+
router.GET("", h.GetAllNews)
106+
router.GET("/:id", h.GetNewsByID)
107+
router.PUT("/:id", h.UpdateNewsByID)
108+
router.DELETE("/:id", h.DeleteNewsByID)
109+
}
110+
111+
func (h *Handler) CreateNews(ctx *gin.Context) {
112+
ctx.Status(http.StatusNotImplemented)
113+
}
114+
115+
func (h *Handler) GetAllNews(ctx *gin.Context) {
116+
ctx.Status(http.StatusNotImplemented)
117+
}
118+
119+
func (h *Handler) GetNewsByID(ctx *gin.Context) {
120+
ctx.Status(http.StatusNotImplemented)
121+
}
122+
123+
func (h *Handler) UpdateNewsByID(ctx *gin.Context) {
124+
ctx.Status(http.StatusNotImplemented)
125+
}
126+
127+
func (h *Handler) DeleteNewsByID(ctx *gin.Context) {
128+
ctx.Status(http.StatusNotImplemented)
129+
}
130+
```
131+
## setup test
132+
```golang
133+
package news_test
134+
135+
import (
136+
"net/http"
137+
"net/http/httptest"
138+
"testing"
139+
140+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/application"
141+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/config"
142+
"github.com/stretchr/testify/assert"
143+
)
144+
145+
func Test_CreateNews(t *testing.T) {
146+
testCases := []struct {
147+
name string
148+
expectedStatus int
149+
}{
150+
{
151+
name: "not implemented",
152+
expectedStatus: http.StatusNotImplemented,
153+
},
154+
}
155+
for _, tc := range testCases {
156+
t.Run(tc.name, func(t *testing.T) {
157+
// Arrange test
158+
app := application.New(config.AppConfig)
159+
app.SetupRoutes()
160+
161+
w := httptest.NewRecorder()
162+
r, _ := http.NewRequest(http.MethodPost, "/news", nil)
163+
// Act
164+
app.Router.ServeHTTP(w, r)
165+
assert.Equalf(t, tc.expectedStatus, w.Code, "expected %v ,got %v", tc.expectedStatus, w.Code)
166+
})
167+
}
168+
}
169+
170+
func Test_GetAllNews(t *testing.T) {
171+
testCases := []struct {
172+
name string
173+
expectedStatus int
174+
}{
175+
{
176+
name: "not implemented",
177+
expectedStatus: http.StatusNotImplemented,
178+
},
179+
}
180+
for _, tc := range testCases {
181+
t.Run(tc.name, func(t *testing.T) {
182+
// Arrange test
183+
app := application.New(config.AppConfig)
184+
app.SetupRoutes()
185+
186+
w := httptest.NewRecorder()
187+
r, _ := http.NewRequest(http.MethodGet, "/news", nil)
188+
// Act
189+
app.Router.ServeHTTP(w, r)
190+
assert.Equalf(t, tc.expectedStatus, w.Code, "expected %v ,got %v", tc.expectedStatus, w.Code)
191+
})
192+
}
193+
}
194+
195+
func Test_GetNewsById(t *testing.T) {
196+
testCases := []struct {
197+
name string
198+
expectedStatus int
199+
}{
200+
{
201+
name: "not implemented",
202+
expectedStatus: http.StatusNotImplemented,
203+
},
204+
}
205+
for _, tc := range testCases {
206+
t.Run(tc.name, func(t *testing.T) {
207+
// Arrange test
208+
app := application.New(config.AppConfig)
209+
app.SetupRoutes()
210+
211+
w := httptest.NewRecorder()
212+
r, _ := http.NewRequest(http.MethodGet, "/news/1", nil)
213+
// Act
214+
app.Router.ServeHTTP(w, r)
215+
assert.Equalf(t, tc.expectedStatus, w.Code, "expected %v ,got %v", tc.expectedStatus, w.Code)
216+
})
217+
}
218+
}
219+
220+
func Test_UpdateNewsByID(t *testing.T) {
221+
testCases := []struct {
222+
name string
223+
expectedStatus int
224+
}{
225+
{
226+
name: "not implemented",
227+
expectedStatus: http.StatusNotImplemented,
228+
},
229+
}
230+
for _, tc := range testCases {
231+
t.Run(tc.name, func(t *testing.T) {
232+
// Arrange test
233+
app := application.New(config.AppConfig)
234+
app.SetupRoutes()
235+
236+
w := httptest.NewRecorder()
237+
r, _ := http.NewRequest(http.MethodPut, "/news/1", nil)
238+
// Act
239+
app.Router.ServeHTTP(w, r)
240+
assert.Equalf(t, tc.expectedStatus, w.Code, "expected %v ,got %v", tc.expectedStatus, w.Code)
241+
})
242+
}
243+
}
244+
245+
func Test_DeleteNewsByID(t *testing.T) {
246+
testCases := []struct {
247+
name string
248+
expectedStatus int
249+
}{
250+
{
251+
name: "not implemented",
252+
expectedStatus: http.StatusNotImplemented,
253+
},
254+
}
255+
for _, tc := range testCases {
256+
t.Run(tc.name, func(t *testing.T) {
257+
// Arrange test
258+
app := application.New(config.AppConfig)
259+
app.SetupRoutes()
260+
261+
w := httptest.NewRecorder()
262+
r, _ := http.NewRequest(http.MethodDelete, "/news/1", nil)
263+
// Act
264+
app.Router.ServeHTTP(w, r)
265+
assert.Equalf(t, tc.expectedStatus, w.Code, "expected %v ,got %v", tc.expectedStatus, w.Code)
266+
})
267+
}
268+
}
269+
```

Taskfile.yaml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
version: '3'
2+
3+
tasks:
4+
default:
5+
cmds:
6+
- echo "This is task cmd"
7+
silent: true
8+
9+
build:
10+
cmds:
11+
- CGO_ENABLED=0 GOOS=linux go build -o bin/main cmd/main.go
12+
silent: true
13+
run:
14+
cmds:
15+
- ./bin/main
16+
deps:
17+
- build
18+
silent: true
19+
coverage:
20+
cmds:
21+
- go test -v -cover ./...
22+
silent: true
23+
test:
24+
cmds:
25+
- go test -v ./...
26+
silent: true
27+

cmd/main.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
10+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/application"
11+
"github.com/leetcode-golang-classroom/golang-rest-api-sample/interanl/config"
12+
)
13+
14+
func main() {
15+
app := application.New(config.AppConfig)
16+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
17+
defer cancel()
18+
err := app.Start(ctx)
19+
if err != nil {
20+
log.Fatalf("failed to start app: %v", err)
21+
}
22+
}

0 commit comments

Comments
 (0)