Skip to content

Commit 4a14132

Browse files
author
Chunting Wu
committed
Go Crash Course 03 is done
0 parents  commit 4a14132

File tree

11 files changed

+795
-0
lines changed

11 files changed

+795
-0
lines changed

controller/post-controller.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package controller
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
"example.com/clean-arch/entity"
8+
"example.com/clean-arch/errors"
9+
"example.com/clean-arch/service"
10+
)
11+
12+
type controller struct{}
13+
14+
var (
15+
postService service.PostService
16+
)
17+
18+
type PostController interface {
19+
GetPosts(response http.ResponseWriter, request *http.Request)
20+
AddPost(response http.ResponseWriter, request *http.Request)
21+
}
22+
23+
func NewPostController(service service.PostService) PostController {
24+
postService = service
25+
return &controller{}
26+
}
27+
28+
func (*controller) GetPosts(response http.ResponseWriter, request *http.Request) {
29+
response.Header().Set("Content-Type", "application/json")
30+
posts, err := postService.FindAll()
31+
if err != nil {
32+
response.WriteHeader(http.StatusInternalServerError)
33+
json.NewEncoder(response).Encode(errors.ServiceError{Message: "Error getting the posts"})
34+
}
35+
response.WriteHeader(http.StatusOK)
36+
json.NewEncoder(response).Encode(posts)
37+
}
38+
39+
func (*controller) AddPost(response http.ResponseWriter, request *http.Request) {
40+
response.Header().Set("Content-Type", "application/json")
41+
var post entity.Post
42+
err := json.NewDecoder(request.Body).Decode(&post)
43+
if err != nil {
44+
response.WriteHeader(http.StatusInternalServerError)
45+
json.NewEncoder(response).Encode(errors.ServiceError{Message: "Error unmarshalling data"})
46+
return
47+
}
48+
49+
err = postService.Validate(&post)
50+
if err != nil {
51+
response.WriteHeader(http.StatusInternalServerError)
52+
json.NewEncoder(response).Encode(errors.ServiceError{Message: err.Error()})
53+
return
54+
}
55+
56+
result, err := postService.Create(&post)
57+
if err != nil {
58+
response.WriteHeader(http.StatusInternalServerError)
59+
json.NewEncoder(response).Encode(errors.ServiceError{Message: "Error saving the post"})
60+
return
61+
}
62+
response.WriteHeader(http.StatusOK)
63+
json.NewEncoder(response).Encode(result)
64+
}

entity/post.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package entity
2+
3+
type Post struct {
4+
ID int64 `json:"id"`
5+
Title string `json:"title"`
6+
Text string `json:"text"`
7+
}

errors/service-error.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package errors
2+
3+
type ServiceError struct {
4+
Message string `json:"message"`
5+
}

go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module example.com/clean-arch
2+
3+
go 1.16
4+
5+
require (
6+
cloud.google.com/go/firestore v1.5.0
7+
github.com/gorilla/mux v1.8.0
8+
google.golang.org/api v0.49.0
9+
)

go.sum

Lines changed: 513 additions & 0 deletions
Large diffs are not rendered by default.

http/mux-router.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package router
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/gorilla/mux"
8+
)
9+
10+
type muxRouter struct{}
11+
12+
var (
13+
muxDispatcher = mux.NewRouter()
14+
)
15+
16+
func NewMuxRouter() Router {
17+
return &muxRouter{}
18+
}
19+
20+
func (*muxRouter) GET(uri string, f func(w http.ResponseWriter, r *http.Request)) {
21+
muxDispatcher.HandleFunc(uri, f).Methods("GET")
22+
}
23+
func (*muxRouter) POST(uri string, f func(w http.ResponseWriter, r *http.Request)) {
24+
muxDispatcher.HandleFunc(uri, f).Methods("POST")
25+
}
26+
func (*muxRouter) SERVE(port string) {
27+
fmt.Printf("Mux HTTP server running on port %v", port)
28+
http.ListenAndServe(port, muxDispatcher)
29+
}

http/router.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package router
2+
3+
import "net/http"
4+
5+
type Router interface {
6+
GET(uri string, f func(w http.ResponseWriter, r *http.Request))
7+
POST(uri string, f func(w http.ResponseWriter, r *http.Request))
8+
SERVE(port string)
9+
}

repository/firestore-repo.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package repository
2+
3+
import (
4+
"context"
5+
"log"
6+
7+
"cloud.google.com/go/firestore"
8+
"example.com/clean-arch/entity"
9+
"google.golang.org/api/iterator"
10+
)
11+
12+
type repo struct{}
13+
14+
func NewFirestoreRepository() PostRepository {
15+
return &repo{}
16+
}
17+
18+
const (
19+
projectId string = "pragmetic-reviews"
20+
collectionName string = "posts"
21+
)
22+
23+
func (*repo) Save(post *entity.Post) (*entity.Post, error) {
24+
ctx := context.Background()
25+
client, err := firestore.NewClient(ctx, projectId)
26+
if err != nil {
27+
log.Fatalf("Failed to create a Firestore Client: %v", err)
28+
return nil, err
29+
}
30+
defer client.Close()
31+
32+
_, _, err = client.Collection(collectionName).Add(ctx, map[string]interface{}{
33+
"ID": post.ID,
34+
"Title": post.Title,
35+
"Text": post.Text,
36+
})
37+
if err != nil {
38+
log.Fatalf("Failed adding a new post: %v", err)
39+
return nil, err
40+
}
41+
return post, nil
42+
}
43+
44+
func (*repo) FindAll() ([]entity.Post, error) {
45+
ctx := context.Background()
46+
client, err := firestore.NewClient(ctx, projectId)
47+
if err != nil {
48+
log.Fatalf("Failed to create a Firestore Client: %v", err)
49+
return nil, err
50+
}
51+
defer client.Close()
52+
53+
var posts []entity.Post
54+
it := client.Collection(collectionName).Documents(ctx)
55+
for {
56+
doc, err := it.Next()
57+
if err == iterator.Done {
58+
break
59+
}
60+
if err != nil {
61+
log.Fatalf("Failed to iterate the list of posts: %v", err)
62+
return nil, err
63+
}
64+
post := entity.Post{
65+
ID: doc.Data()["ID"].(int64),
66+
Title: doc.Data()["Title"].(string),
67+
Text: doc.Data()["Text"].(string),
68+
}
69+
posts = append(posts, post)
70+
}
71+
return posts, nil
72+
}

repository/posts-repo.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package repository
2+
3+
import (
4+
"example.com/clean-arch/entity"
5+
)
6+
7+
type PostRepository interface {
8+
Save(post *entity.Post) (*entity.Post, error)
9+
FindAll() ([]entity.Post, error)
10+
}

server.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"example.com/clean-arch/controller"
8+
router "example.com/clean-arch/http"
9+
"example.com/clean-arch/repository"
10+
"example.com/clean-arch/service"
11+
)
12+
13+
var (
14+
httpRouter router.Router = router.NewMuxRouter()
15+
postRepository repository.PostRepository = repository.NewFirestoreRepository()
16+
postService service.PostService = service.NewPostService(postRepository)
17+
postController controller.PostController = controller.NewPostController(postService)
18+
)
19+
20+
func main() {
21+
const port string = ":8000"
22+
23+
httpRouter.GET("/", func(w http.ResponseWriter, r *http.Request) {
24+
fmt.Fprintln(w, "Up and running...")
25+
})
26+
httpRouter.GET("/posts", postController.GetPosts)
27+
httpRouter.POST("/posts", postController.AddPost)
28+
httpRouter.SERVE(port)
29+
30+
}

0 commit comments

Comments
 (0)