8 changed files with 314 additions and 131 deletions
@ -1,7 +1,7 @@ |
|||||
# ---> VisualStudioCode |
|
||||
.vscode/* |
.vscode/* |
||||
!.vscode/settings.json |
!.vscode/settings.json |
||||
!.vscode/tasks.json |
!.vscode/tasks.json |
||||
!.vscode/launch.json |
!.vscode/launch.json |
||||
!.vscode/extensions.json |
!.vscode/extensions.json |
||||
|
vendor/* |
||||
|
blog |
||||
|
|||||
@ -0,0 +1,39 @@ |
|||||
|
package driver |
||||
|
|
||||
|
import ( |
||||
|
"database/sql" |
||||
|
"fmt" |
||||
|
|
||||
|
_ "github.com/go-sql-driver/mysql" |
||||
|
) |
||||
|
|
||||
|
// DB ...
|
||||
|
type DB struct { |
||||
|
SQL *sql.DB |
||||
|
// Mgo *mgo.database
|
||||
|
} |
||||
|
|
||||
|
var dbConn = &DB{} |
||||
|
|
||||
|
// ConnectSQL ...
|
||||
|
func ConnectSQL(host, port, uname, pass, dbname string) (*DB, error) { |
||||
|
dbSource := fmt.Sprintf( |
||||
|
"%s:%s@tcp(%s:%s)/%s?charset=utf8", |
||||
|
uname, |
||||
|
pass, |
||||
|
host, |
||||
|
port, |
||||
|
dbname, |
||||
|
) |
||||
|
d, err := sql.Open("mysql", dbSource) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
dbConn.SQL = d |
||||
|
return dbConn, err |
||||
|
} |
||||
|
|
||||
|
// connectMongo ...
|
||||
|
func connectMongo(host, port, uname, pass string) error { |
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
package handler |
||||
|
|
||||
|
import ( |
||||
|
"blog/driver" |
||||
|
models "blog/models" |
||||
|
repository "blog/repository" |
||||
|
post "blog/repository/post" |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
"net/http" |
||||
|
"strconv" |
||||
|
|
||||
|
"github.com/go-chi/chi" |
||||
|
) |
||||
|
|
||||
|
// NewPostHandler ...
|
||||
|
func NewPostHandler(db *driver.DB) *Post { |
||||
|
return &Post{ |
||||
|
repo: post.NewSQLPostRepo(db.SQL), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Post ...
|
||||
|
type Post struct { |
||||
|
repo repository.PostRepo |
||||
|
} |
||||
|
|
||||
|
// Fetch ...
|
||||
|
func (p *Post) Fetch(w http.ResponseWriter, r *http.Request) { |
||||
|
payload, _ := p.repo.Fetch(r.Context(), 25) |
||||
|
|
||||
|
respondwithJSON(w, http.StatusOK, payload) |
||||
|
} |
||||
|
|
||||
|
// Create a new post
|
||||
|
func (p *Post) Create(w http.ResponseWriter, r *http.Request) { |
||||
|
post := models.Post{} |
||||
|
json.NewDecoder(r.Body).Decode(&post) |
||||
|
|
||||
|
newID, err := p.repo.Create(r.Context(), &post) |
||||
|
fmt.Println(newID) |
||||
|
if err != nil { |
||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error") |
||||
|
} |
||||
|
respondwithJSON(w, http.StatusCreated, map[string]string{"message": "Successfully Created"}) |
||||
|
} |
||||
|
|
||||
|
// Update a post by id
|
||||
|
func (p *Post) Update(w http.ResponseWriter, r *http.Request) { |
||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id")) |
||||
|
data := models.Post{ID: int(id)} |
||||
|
json.NewDecoder(r.Body).Decode(&data) |
||||
|
payload, err := p.repo.Update(r.Context(), &data) |
||||
|
|
||||
|
if err != nil { |
||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error") |
||||
|
} |
||||
|
|
||||
|
respondwithJSON(w, http.StatusOK, payload) |
||||
|
} |
||||
|
|
||||
|
// GetByID returns a post details
|
||||
|
func (p *Post) GetByID(w http.ResponseWriter, r *http.Request) { |
||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id")) |
||||
|
payload, err := p.repo.GetByID(r.Context(), int64(id)) |
||||
|
|
||||
|
if err != nil { |
||||
|
respondWithError(w, http.StatusNoContent, "Content not found") |
||||
|
} |
||||
|
|
||||
|
respondwithJSON(w, http.StatusOK, payload) |
||||
|
} |
||||
|
|
||||
|
// Delete a post
|
||||
|
func (p *Post) Delete(w http.ResponseWriter, r *http.Request) { |
||||
|
id, _ := strconv.Atoi(chi.URLParam(r, "id")) |
||||
|
_, err := p.repo.Delete(r.Context(), int64(id)) |
||||
|
|
||||
|
if err != nil { |
||||
|
respondWithError(w, http.StatusInternalServerError, "Server Error") |
||||
|
} |
||||
|
|
||||
|
respondwithJSON(w, http.StatusMovedPermanently, map[string]string{"message": "Delete Successfully"}) |
||||
|
} |
||||
|
|
||||
|
// respondwithJSON write json response format
|
||||
|
func respondwithJSON(w http.ResponseWriter, code int, payload interface{}) { |
||||
|
response, _ := json.Marshal(payload) |
||||
|
|
||||
|
w.Header().Set("Content-Type", "application/json") |
||||
|
w.WriteHeader(code) |
||||
|
w.Write(response) |
||||
|
} |
||||
|
|
||||
|
// respondwithError return error message
|
||||
|
func respondWithError(w http.ResponseWriter, code int, msg string) { |
||||
|
respondwithJSON(w, code, map[string]string{"message": msg}) |
||||
|
} |
||||
@ -1,146 +1,42 @@ |
|||||
package main |
package main |
||||
|
|
||||
import ( |
import ( |
||||
"database/sql" |
"blog/driver" |
||||
"encoding/json" |
ph "blog/handler/http" |
||||
"fmt" |
"fmt" |
||||
"net/http" |
"net/http" |
||||
|
"os" |
||||
|
|
||||
"github.com/go-chi/chi" |
|
||||
"github.com/go-chi/chi/middleware" |
"github.com/go-chi/chi/middleware" |
||||
_ "github.com/go-sql-driver/mysql" |
|
||||
) |
|
||||
|
|
||||
var router *chi.Mux |
|
||||
var db *sql.DB |
|
||||
|
|
||||
const ( |
"github.com/go-chi/chi" |
||||
dbName = "go-mysql-crud" |
|
||||
dbPass = "" |
|
||||
dbHost = "localhost" |
|
||||
dbPort = "3306" |
|
||||
) |
) |
||||
|
|
||||
func routers() *chi.Mux { |
func main() { |
||||
router.Get("/posts", AllPosts) |
dbName := os.Getenv("DB_NAME") |
||||
router.Get("/posts/{id}", DetailPost) |
dbPass := os.Getenv("DB_PASS") |
||||
router.Post("/posts", CreatePost) |
dbHost := os.Getenv("DB_HOST") |
||||
router.Put("/posts/{id}", UpdatePost) |
dbPort := os.Getenv("DB_PORT") |
||||
router.Delete("/posts/{id}", DeletePost) |
|
||||
|
|
||||
return router |
|
||||
} |
|
||||
|
|
||||
func init() { |
|
||||
router = chi.NewRouter() |
|
||||
router.Use(middleware.Recoverer) |
|
||||
|
|
||||
dbSource := fmt.Sprintf("root:%s@tcp(%s:%s)/%s?charset=utf8", dbPass, dbHost, dbPort, dbName) |
|
||||
fmt.Println(dbSource) |
|
||||
var err error |
|
||||
db, err = sql.Open("mysql", dbSource) |
|
||||
|
|
||||
catch(err) |
|
||||
} |
|
||||
|
|
||||
// Post ...
|
|
||||
type Post struct { |
|
||||
ID int `json:"id"` |
|
||||
Title string `json:"title"` |
|
||||
Content string `json:"content"` |
|
||||
} |
|
||||
|
|
||||
// CreatePost ...
|
|
||||
func CreatePost(w http.ResponseWriter, r *http.Request) { |
|
||||
var post Post |
|
||||
json.NewDecoder(r.Body).Decode(&post) |
|
||||
|
|
||||
query, err := db.Prepare("Insert posts SET title=?, content=?") |
|
||||
catch(err) |
|
||||
|
|
||||
_, er := query.Exec(post.Title, post.Content) |
|
||||
catch(er) |
|
||||
defer query.Close() |
|
||||
|
|
||||
respondwithJSON(w, http.StatusCreated, map[string]string{"message": "successfully created"}) |
|
||||
} |
|
||||
|
|
||||
// UpdatePost ...
|
|
||||
func UpdatePost(w http.ResponseWriter, r *http.Request) { |
|
||||
var post Post |
|
||||
id := chi.URLParam(r, "id") |
|
||||
json.NewDecoder(r.Body).Decode(&post) |
|
||||
|
|
||||
query, err := db.Prepare("Update posts set title=?, content=? where id=?") |
|
||||
catch(err) |
|
||||
_, er := query.Exec(post.Title, post.Content, id) |
|
||||
catch(er) |
|
||||
|
|
||||
defer query.Close() |
|
||||
|
|
||||
respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"}) |
|
||||
} |
|
||||
|
|
||||
// DeletePost ...
|
|
||||
func DeletePost(w http.ResponseWriter, r *http.Request) { |
|
||||
id := chi.URLParam(r, "id") |
|
||||
|
|
||||
query, err := db.Prepare("delete from post where id=?") |
|
||||
catch(err) |
|
||||
_, er := query.Exec(id) |
|
||||
catch(er) |
|
||||
query.Close() |
|
||||
|
|
||||
respondwithJSON(w, http.StatusOK, map[string]string{"message": "deleted successfully"}) |
|
||||
} |
|
||||
|
|
||||
// AllPosts ...
|
|
||||
func AllPosts(w http.ResponseWriter, r *http.Request) { |
|
||||
errors := []error{} |
|
||||
payload := []Post{} |
|
||||
|
|
||||
rows, err := db.Query("Select id, title, content From posts") |
|
||||
catch(err) |
|
||||
|
|
||||
defer rows.Close() |
|
||||
|
|
||||
for rows.Next() { |
|
||||
data := Post{} |
|
||||
|
|
||||
er := rows.Scan(&data.ID, &data.Title, &data.Content) |
|
||||
|
|
||||
if er != nil { |
|
||||
errors = append(errors, er) |
|
||||
} |
|
||||
payload = append(payload, data) |
|
||||
} |
|
||||
|
|
||||
respondwithJSON(w, http.StatusOK, payload) |
|
||||
} |
|
||||
|
|
||||
// DetailPost ...
|
|
||||
func DetailPost(w http.ResponseWriter, r *http.Request) { |
|
||||
payload := Post{} |
|
||||
id := chi.URLParam(r, "id") |
|
||||
|
|
||||
row := db.QueryRow("Select if, title, content From posts where id=?", id) |
|
||||
|
|
||||
err := row.Scan( |
println("this is db") |
||||
&payload.ID, |
|
||||
&payload.Title, |
|
||||
&payload.Content, |
|
||||
) |
|
||||
|
|
||||
|
connection, err := driver.ConnectSQL(dbHost, dbPort, "root", dbPass, dbName) |
||||
if err != nil { |
if err != nil { |
||||
respondWithError(w, http.StatusNotFound, "no rows in result set") |
fmt.Println(err) |
||||
return |
os.Exit(-1) |
||||
} |
} |
||||
|
|
||||
respondwithJSON(w, http.StatusOK, payload) |
r := chi.NewRouter() |
||||
} |
r.Use(middleware.Recoverer) |
||||
|
r.Use(middleware.Logger) |
||||
|
|
||||
//
|
pHandler := ph.NewPostHandler(connection) |
||||
func main() { |
r.Get("/posts", pHandler.Fetch) |
||||
routers() |
r.Get("/posts/{id}", pHandler.GetByID) |
||||
http.ListenAndServe(":8005", Logger()) |
r.Post("/posts", pHandler.Create) |
||||
|
r.Put("/posts/{id}", pHandler.Update) |
||||
|
r.Delete("/posts/{id}", pHandler.Delete) |
||||
|
|
||||
|
fmt.Println("Server listen at :8005") |
||||
|
http.ListenAndServe(":8005", r) |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,8 @@ |
|||||
|
package models |
||||
|
|
||||
|
import "errors" |
||||
|
|
||||
|
var ( |
||||
|
// ErrNotFound ...
|
||||
|
ErrNotFound = errors.New("requested item is not found") |
||||
|
) |
||||
@ -0,0 +1,8 @@ |
|||||
|
package models |
||||
|
|
||||
|
// Post ...
|
||||
|
type Post struct { |
||||
|
ID int `json:"id"` |
||||
|
Title string `json:"title"` |
||||
|
Content string `json:"content"` |
||||
|
} |
||||
@ -0,0 +1,118 @@ |
|||||
|
package post |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"database/sql" |
||||
|
|
||||
|
models "blog/models" |
||||
|
pRepo "blog/repository" |
||||
|
) |
||||
|
|
||||
|
// NewSQLPostRepo ...
|
||||
|
func NewSQLPostRepo(Conn *sql.DB) pRepo.PostRepo { |
||||
|
return &mysqlPostRepo{ |
||||
|
Conn: Conn, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type mysqlPostRepo struct { |
||||
|
Conn *sql.DB |
||||
|
} |
||||
|
|
||||
|
// fetch ...
|
||||
|
func (m *mysqlPostRepo) fetch(ctx context.Context, query string, args ...interface{}) ([]*models.Post, error) { |
||||
|
rows, err := m.Conn.QueryContext(ctx, query, args...) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer rows.Close() |
||||
|
|
||||
|
payload := make([]*models.Post, 0) |
||||
|
for rows.Next() { |
||||
|
data := new(models.Post) |
||||
|
err := rows.Scan( |
||||
|
&data.ID, |
||||
|
&data.Title, |
||||
|
&data.Content, |
||||
|
) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
payload = append(payload, data) |
||||
|
} |
||||
|
return payload, nil |
||||
|
} |
||||
|
|
||||
|
func (m *mysqlPostRepo) Fetch(ctx context.Context, num int64) ([]*models.Post, error) { |
||||
|
query := "Select id, title, content From posts limit ?" |
||||
|
|
||||
|
return m.fetch(ctx, query, num) |
||||
|
} |
||||
|
|
||||
|
func (m *mysqlPostRepo) GetByID(ctx context.Context, id int64) (*models.Post, error) { |
||||
|
query := "Select id, title, content From posts where id=?" |
||||
|
|
||||
|
rows, err := m.fetch(ctx, query, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
payload := &models.Post{} |
||||
|
if len(rows) > 0 { |
||||
|
payload = rows[0] |
||||
|
} else { |
||||
|
return nil, models.ErrNotFound |
||||
|
} |
||||
|
return payload, nil |
||||
|
} |
||||
|
|
||||
|
func (m *mysqlPostRepo) Create(ctx context.Context, p *models.Post) (int64, error) { |
||||
|
query := "Insert posts SET title=?, content=?" |
||||
|
|
||||
|
stmt, err := m.Conn.PrepareContext(ctx, query) |
||||
|
if err != nil { |
||||
|
return -1, err |
||||
|
} |
||||
|
|
||||
|
res, err := stmt.ExecContext(ctx, p.Title, p.Content) |
||||
|
defer stmt.Close() |
||||
|
|
||||
|
if err != nil { |
||||
|
return -1, err |
||||
|
} |
||||
|
return res.LastInsertId() |
||||
|
} |
||||
|
|
||||
|
func (m *mysqlPostRepo) Update(ctx context.Context, p *models.Post) (*models.Post, error) { |
||||
|
query := "Update posts set title=?, content=? where id=?" |
||||
|
|
||||
|
stmt, err := m.Conn.PrepareContext(ctx, query) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
_, err = stmt.ExecContext( |
||||
|
ctx, |
||||
|
p.Title, |
||||
|
p.Content, |
||||
|
p.ID, |
||||
|
) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer stmt.Close() |
||||
|
return p, nil |
||||
|
} |
||||
|
|
||||
|
func (m *mysqlPostRepo) Delete(ctx context.Context, id int64) (bool, error) { |
||||
|
query := "Delete From posts Where id=?" |
||||
|
|
||||
|
stmt, err := m.Conn.PrepareContext(ctx, query) |
||||
|
if err != nil { |
||||
|
return false, err |
||||
|
} |
||||
|
_, err = stmt.ExecContext(ctx, id) |
||||
|
if err != nil { |
||||
|
return false, err |
||||
|
} |
||||
|
defer stmt.Close() |
||||
|
return true, nil |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package repository |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"blog/models" |
||||
|
) |
||||
|
|
||||
|
// PostRepo explain...
|
||||
|
type PostRepo interface { |
||||
|
Fetch(ctx context.Context, num int64) ([]*models.Post, error) |
||||
|
GetByID(ctx context.Context, id int64) (*models.Post, error) |
||||
|
Create(ctx context.Context, p *models.Post) (int64, error) |
||||
|
Update(ctx context.Context, p *models.Post) (*models.Post, error) |
||||
|
Delete(ctx context.Context, id int64) (bool, error) |
||||
|
} |
||||
Loading…
Reference in new issue