From 85af1381ef746771680f951695f321f5b8ab77a5 Mon Sep 17 00:00:00 2001 From: Ruidy Nemausat Date: Mon, 16 Mar 2020 19:00:03 +0100 Subject: [PATCH] mvc --- README.md | 2 +- TODO.md | 4 +- controller/router.go | 88 ------------------- .../controllers_test.go | 6 +- controllers/home.go | 41 +++++++++ controllers/page.go | 37 ++++++++ controllers/router.go | 30 +++++++ data/{ => files}/TestPage.txt | 0 data/{ => files}/chocolate.txt | 0 data/{ => files}/doc.txt | 5 +- data/{ => files}/test.txt | 0 data/psql.go | 39 +++----- data/userStore.go | 64 ++++++++------ main.go | 4 +- model/page_test.go => models/models_test.go | 2 +- {model => models}/page.go | 6 +- {model => models}/user.go | 2 +- {service => services}/mail.go | 2 +- {service => services}/payment.go | 4 +- views/render.go | 17 +++- 20 files changed, 190 insertions(+), 163 deletions(-) delete mode 100644 controller/router.go rename controller/controller_test.go => controllers/controllers_test.go (74%) create mode 100644 controllers/home.go create mode 100644 controllers/page.go create mode 100644 controllers/router.go rename data/{ => files}/TestPage.txt (100%) rename data/{ => files}/chocolate.txt (100%) rename data/{ => files}/doc.txt (67%) rename data/{ => files}/test.txt (100%) rename model/page_test.go => models/models_test.go (97%) rename {model => models}/page.go (91%) rename {model => models}/user.go (98%) rename {service => services}/mail.go (98%) rename {service => services}/payment.go (88%) diff --git a/README.md b/README.md index 4141f84..d09ef8d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Go-Wiki -Wiki web application model built using `Go` +Wiki web application models built using `Go` diff --git a/TODO.md b/TODO.md index ba5bd46..7d48e11 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,5 @@ # TO DO - [x] The mail field is case sensitive. Convert mail to lowercase before they are sent to InsertUser -- [] All errors are fatal. Provide proper error handling. -- [] Refactor UserStore code +- [ ] All errors are fatal. Provide proper error handling. +- [ ] Refactor UserStore code diff --git a/controller/router.go b/controller/router.go deleted file mode 100644 index 2b88949..0000000 --- a/controller/router.go +++ /dev/null @@ -1,88 +0,0 @@ -package controller - -import ( - "fmt" - "net/http" - "regexp" - - "github.com/rjNemo/go-wiki/model" - "github.com/rjNemo/go-wiki/service" - "github.com/rjNemo/go-wiki/views" -) - -var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$") - -// Router dispatch the request to the corresponding route handlers. -func Router() { - // http.HandleFunc("/", loveHandler) - http.HandleFunc("/view/", makeHandler(viewHandler)) - http.HandleFunc("/edit/", makeHandler(editHandler)) - http.HandleFunc("/save/", makeHandler(saveHandler)) - http.HandleFunc("/contact/", contactHandler) - http.HandleFunc("/contact/post/", postContactHandler) - http.HandleFunc("/", homeHandler) -} - -func homeHandler(w http.ResponseWriter, r *http.Request) { - views.Template(w, "home", nil) -} - -func viewHandler(w http.ResponseWriter, r *http.Request, title string) { - p, err := model.LoadPage(title) - if err != nil { - http.Redirect(w, r, "/edit/"+title, http.StatusFound) - return - } - views.Template(w, "view", p) -} - -func editHandler(w http.ResponseWriter, r *http.Request, title string) { - p, err := model.LoadPage(title) - if err != nil { - p = model.NewPage(title, nil) - } - views.Template(w, "edit", p) -} - -func saveHandler(w http.ResponseWriter, r *http.Request, title string) { - body := r.FormValue("body") - p := model.NewPage(title, []byte(body)) - err := p.Save() - checkError(err, w) - http.Redirect(w, r, "/view/"+title, http.StatusFound) -} - -func contactHandler(w http.ResponseWriter, r *http.Request) { - views.Template(w, "contact", nil) -} - -func postContactHandler(w http.ResponseWriter, r *http.Request) { - err := r.ParseForm() - checkError(err, w) // bad error handling - mail := parseContactForm(r) - mail.Send() - fmt.Println(mail) - views.Template(w, "contact_sent", nil) -} - -func parseContactForm(r *http.Request) service.Mail { - return service.NewMail(r.PostFormValue("email"), r.PostFormValue("message")) -} - -func checkError(err error, w http.ResponseWriter) { - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - m := validPath.FindStringSubmatch(r.URL.Path) - if m == nil { - http.NotFound(w, r) - return - } - fn(w, r, m[2]) - } -} diff --git a/controller/controller_test.go b/controllers/controllers_test.go similarity index 74% rename from controller/controller_test.go rename to controllers/controllers_test.go index 048545d..f51e72f 100644 --- a/controller/controller_test.go +++ b/controllers/controllers_test.go @@ -1,11 +1,11 @@ -package controller +package controllers import ( "io" "net/http" "testing" - "github.com/rjNemo/go-wiki/service" + "github.com/rjNemo/go-wiki/services" ) func TestParseContactForm(t *testing.T) { @@ -15,7 +15,7 @@ func TestParseContactForm(t *testing.T) { t.Errorf("%s", err) } ans := parseContactForm(r) - if ans != service.NewMail("", "") { + if ans != services.NewMail("", "") { t.Errorf("parseContactForm(r) = %v", ans) } } diff --git a/controllers/home.go b/controllers/home.go new file mode 100644 index 0000000..636bb82 --- /dev/null +++ b/controllers/home.go @@ -0,0 +1,41 @@ +package controllers + +import ( + "log" + "net/http" + + "github.com/rjNemo/go-wiki/services" + "github.com/rjNemo/go-wiki/views" +) + +// type HomeHandler struct { +// } + +func homeHandler(w http.ResponseWriter, r *http.Request) { + views.Template(w, "home", nil) +} + +func contactHandler(w http.ResponseWriter, r *http.Request) { + views.Template(w, "contact", nil) +} + +func postContactHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + checkError(err, w) // bad error handling + mail := parseContactForm(r) + // mail.Send() + log.Println(mail) + views.Template(w, "contact_sent", nil) +} + +func parseContactForm(r *http.Request) services.Mail { + log.Println(r.PostForm) + return services.NewMail(r.PostFormValue("email"), r.PostFormValue("message")) +} + +func checkError(err error, w http.ResponseWriter) { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/controllers/page.go b/controllers/page.go new file mode 100644 index 0000000..8ab9624 --- /dev/null +++ b/controllers/page.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "net/http" + + "github.com/rjNemo/go-wiki/models" + "github.com/rjNemo/go-wiki/views" +) + +// type PageHandler struct { +// Pages data.PageStore +// } + +func viewHandler(w http.ResponseWriter, r *http.Request, title string) { + p, err := models.LoadPage(title) + if err != nil { + http.Redirect(w, r, "/edit/"+title, http.StatusFound) + return + } + views.Template(w, "view", p) +} + +func editHandler(w http.ResponseWriter, r *http.Request, title string) { + p, err := models.LoadPage(title) + if err != nil { + p = models.NewPage(title, nil) + } + views.Template(w, "edit", p) +} + +func saveHandler(w http.ResponseWriter, r *http.Request, title string) { + body := r.FormValue("body") + p := models.NewPage(title, []byte(body)) + err := p.Save() + checkError(err, w) + http.Redirect(w, r, "/view/"+title, http.StatusFound) +} diff --git a/controllers/router.go b/controllers/router.go new file mode 100644 index 0000000..39ffb6e --- /dev/null +++ b/controllers/router.go @@ -0,0 +1,30 @@ +package controllers + +import ( + "net/http" + "regexp" +) + +// Router dispatch the request to the corresponding route handlers. +func Router() { + // http.HandleFunc("/", loveHandler) + http.HandleFunc("/view/", makeHandler(viewHandler)) + http.HandleFunc("/edit/", makeHandler(editHandler)) + http.HandleFunc("/save/", makeHandler(saveHandler)) + http.HandleFunc("/contact/", contactHandler) + http.HandleFunc("/contact/post/", postContactHandler) + http.HandleFunc("/", homeHandler) +} + +func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + m := validPath.FindStringSubmatch(r.URL.Path) + if m == nil { + http.NotFound(w, r) + return + } + fn(w, r, m[2]) + } +} + +var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$") diff --git a/data/TestPage.txt b/data/files/TestPage.txt similarity index 100% rename from data/TestPage.txt rename to data/files/TestPage.txt diff --git a/data/chocolate.txt b/data/files/chocolate.txt similarity index 100% rename from data/chocolate.txt rename to data/files/chocolate.txt diff --git a/data/doc.txt b/data/files/doc.txt similarity index 67% rename from data/doc.txt rename to data/files/doc.txt index 938475e..bb2ef60 100644 --- a/data/doc.txt +++ b/data/files/doc.txt @@ -1,7 +1,8 @@ -Type a term in the search bar. + Type a term in the search bar. If it already exist you will reach this page. If it is not yet defined you will have the possibility to create it. -Have fun \ No newline at end of file +Have fun + \ No newline at end of file diff --git a/data/test.txt b/data/files/test.txt similarity index 100% rename from data/test.txt rename to data/files/test.txt diff --git a/data/psql.go b/data/psql.go index 7faf3eb..218263b 100644 --- a/data/psql.go +++ b/data/psql.go @@ -5,7 +5,7 @@ import ( "log" _ "github.com/lib/pq" // postgresql database package - "github.com/rjNemo/go-wiki/model" + "github.com/rjNemo/go-wiki/models" "github.com/rjNemo/go-wiki/settings" ) @@ -24,41 +24,28 @@ func UsePSQL() { log.Fatal(err) } - log.Println("Database successfully connected!") + log.Println("Connection to database successfully established!") - createUserTable(db) store := NewUserStore(db) - // u := model.TestUser() + store.CreateTable() + // u := models.TestUser() // store.Add(u) // log.Print(u) - u1 := model.NewUser(3, 20, "paul", "newman", "PdsNz@FDKML.COM") - store.Update(16, u1) - log.Println(store.Get(1)) + // u1 := models.NewUser(3, 20, "paul", "newman", "PdsNz@FDKML.COM") + // store.Update(16, u1) + // log.Println(store.Get(1)) // store.Delete(8) - log.Println(store.GetAll()) - log.Println(store.Find("first_name", "John")) -} - -func sqlExec(db *sql.DB, s string) { - if _, err := db.Exec(s); err != nil { - log.Fatal(err) - return - } - log.Printf("Command successfully executed!: %s", s) -} - -func createUserTable(db *sql.DB) { - sqlExec(db, QueryCreateTable) - log.Print("Table successfully created!") + // log.Println(store.GetAll()) + // log.Println(store.Find("first_name", "John")) } // Store interface defines the methods any store must satisfy type Store interface { - // CreateTable() + CreateTable() Add(i interface{}) - Get(id int) (model.User, error) - GetAll(id int) ([]model.User, error) + Get(id int) (models.User, error) + GetAll(id int) ([]models.User, error) Delete(id int) - Find(ex string) + // Find(ex string) Update(id int, i interface{}) } diff --git a/data/userStore.go b/data/userStore.go index 8a3a52a..dc7db6b 100644 --- a/data/userStore.go +++ b/data/userStore.go @@ -4,7 +4,7 @@ import ( "database/sql" "log" - "github.com/rjNemo/go-wiki/model" + "github.com/rjNemo/go-wiki/models" ) // UserStore is used to perform user-related CRUD operations on the DB @@ -17,8 +17,16 @@ func NewUserStore(db *sql.DB) UserStore { return UserStore{db: db} } +// CreateTable creates the user table in the database if it exits not yet. +func (us UserStore) CreateTable() { + if _, err := us.db.Exec(QueryCreateTable); err != nil { + log.Fatal(err) + } + // log.Print("Table successfully created!") +} + // GetAll retrieves all the users from the database. -func (us UserStore) GetAll() ([]model.User, error) { +func (us UserStore) GetAll() ([]models.User, error) { var id, age int var firstName, lastName, email string @@ -27,42 +35,42 @@ func (us UserStore) GetAll() ([]model.User, error) { return nil, err } - var users []model.User + var users []models.User for rows.Next() { err := rows.Scan(&id, &age, &firstName, &lastName, &email) if err != nil { log.Fatal(err) // too severe } - u := model.NewUser(id, age, firstName, lastName, email) + u := models.NewUser(id, age, firstName, lastName, email) users = append(users, u) } return users, nil } // Find retrieves a user from the database using an expression -func (us UserStore) Find(k interface{}, v interface{}) ([]model.User, error) { - var id, age int - var firstName, lastName, email string +// func (us UserStore) Find(k interface{}, v interface{}) ([]models.User, error) { +// var id, age int +// var firstName, lastName, email string - rows, err := us.db.Query(QueryFind, k, v) - if err != nil { - return nil, err - } +// rows, err := us.db.Query(QueryFind, k, v) +// if err != nil { +// return nil, err +// } - var users []model.User - for rows.Next() { - err := rows.Scan(&id, &age, &firstName, &lastName, &email) - if err != nil { - log.Fatal(err) // too severe - } - u := model.NewUser(id, age, firstName, lastName, email) - users = append(users, u) - } - return users, nil -} +// var users []models.User +// for rows.Next() { +// err := rows.Scan(&id, &age, &firstName, &lastName, &email) +// if err != nil { +// log.Fatal(err) // too severe +// } +// u := models.NewUser(id, age, firstName, lastName, email) +// users = append(users, u) +// } +// return users, nil +// } // Get retrieves the user identified by 'id' from database -func (us UserStore) Get(uid int) (model.User, error) { +func (us UserStore) Get(uid int) (models.User, error) { var id, age int var firstName, lastName, email string @@ -70,17 +78,17 @@ func (us UserStore) Get(uid int) (model.User, error) { switch err := row.Scan(&id, &age, &firstName, &lastName, &email); err { case sql.ErrNoRows: log.Println("No entry returned") - return model.User{}, err + return models.User{}, err case nil: - return model.NewUser(id, age, firstName, lastName, email), nil + return models.NewUser(id, age, firstName, lastName, email), nil default: log.Fatal(err) - return model.User{}, err + return models.User{}, err } } // Add inserts a user in the database. -func (us UserStore) Add(u model.User) { +func (us UserStore) Add(u models.User) { id := 0 err := us.db.QueryRow(QueryInsert, u.Age(), u.Email(), u.FirstName(), u.LastName()).Scan(&id) if err != nil { @@ -90,7 +98,7 @@ func (us UserStore) Add(u model.User) { } // Update edits user identified by 'id' in the database -func (us UserStore) Update(id int, u model.User) { +func (us UserStore) Update(id int, u models.User) { res, err := us.db.Exec(QueryUpdate, id, u.Age(), u.Email(), u.FirstName(), u.LastName()) if err != nil { log.Fatal(err) // too severe diff --git a/main.go b/main.go index ecbcf49..d666618 100644 --- a/main.go +++ b/main.go @@ -4,12 +4,14 @@ import ( "log" "net/http" + "github.com/rjNemo/go-wiki/controllers" "github.com/rjNemo/go-wiki/data" + "github.com/rjNemo/go-wiki/settings" ) func main() { data.UsePSQL() - // startServer(settings.Port, controller.Router) + startServer(settings.Port, controllers.Router) } func startServer(p string, r func()) { diff --git a/model/page_test.go b/models/models_test.go similarity index 97% rename from model/page_test.go rename to models/models_test.go index 11faeb4..8793740 100644 --- a/model/page_test.go +++ b/models/models_test.go @@ -1,4 +1,4 @@ -package model +package models import "testing" diff --git a/model/page.go b/models/page.go similarity index 91% rename from model/page.go rename to models/page.go index 66e2e8a..86aff14 100644 --- a/model/page.go +++ b/models/page.go @@ -1,4 +1,4 @@ -package model +package models import ( "io/ioutil" @@ -42,13 +42,13 @@ func NewPage(title string, body []byte) *Page { // Save a page to the 'data/' folder in txt format. func (p *Page) Save() error { - fileName := "data/" + p.title + ".txt" + fileName := "data/files/" + p.title + ".txt" return ioutil.WriteFile(fileName, p.body, 0600) } // LoadPage reads a saved page data and returns a pointer to the Page. func LoadPage(title string) (*Page, error) { - fileName := "data/" + title + ".txt" + fileName := "data/files/" + title + ".txt" body, err := ioutil.ReadFile(fileName) if err != nil { return nil, err diff --git a/model/user.go b/models/user.go similarity index 98% rename from model/user.go rename to models/user.go index a5a8149..9bce789 100644 --- a/model/user.go +++ b/models/user.go @@ -1,4 +1,4 @@ -package model +package models import "strings" diff --git a/service/mail.go b/services/mail.go similarity index 98% rename from service/mail.go rename to services/mail.go index d1baf43..9b41c8d 100644 --- a/service/mail.go +++ b/services/mail.go @@ -1,4 +1,4 @@ -package service +package services import ( "fmt" diff --git a/service/payment.go b/services/payment.go similarity index 88% rename from service/payment.go rename to services/payment.go index 7b418cf..f4a19c8 100644 --- a/service/payment.go +++ b/services/payment.go @@ -1,4 +1,4 @@ -package service +package services import ( "github.com/stripe/stripe-go" @@ -8,7 +8,7 @@ import ( // secret API key var secretKey string = "sk_test_dyp7eLEq2KnxpWE0qIMihTYZ" -// service.PaymentIntent(1000, "ruidy.nemausat@gmail.com") +// services.PaymentIntent(1000, "ruidy.nemausat@gmail.com") // PaymentIntent creates a payment intent func PaymentIntent(a int64, m string) (*stripe.PaymentIntent, error) { diff --git a/views/render.go b/views/render.go index a7dd4bc..ff9635d 100644 --- a/views/render.go +++ b/views/render.go @@ -1,19 +1,28 @@ package views import ( + "log" "net/http" "text/template" - "github.com/rjNemo/go-wiki/model" + "github.com/rjNemo/go-wiki/models" "github.com/rjNemo/go-wiki/settings" ) -func Template(w http.ResponseWriter, tmpl string, p *model.Page) { +func Template(w http.ResponseWriter, tmpl string, p *models.Page) { // err := templates.ExecuteTemplate(w, "templates/"+tmpl+".html", p) t, err := template.ParseFiles(getTmplName("base"), getTmplName(tmpl)) - checkError(err, w) + + if err != nil { + log.Println(err.Error()) + return + } + err = t.Execute(w, p) - checkError(err, w) + if err != nil { + log.Println(err.Error()) + return + } } func getTmplName(tmpl string) string {