This commit is contained in:
Ruidy Nemausat 2020-03-16 19:00:03 +01:00
parent db947b17f3
commit 85af1381ef
20 changed files with 190 additions and 163 deletions

View file

@ -1,6 +1,6 @@
# Go-Wiki
Wiki web application model built using `Go`
Wiki web application models built using `Go`
<!-- img.shields.io -->

View file

@ -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

View file

@ -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])
}
}

View file

@ -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)
}
}

41
controllers/home.go Normal file
View file

@ -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
}
}

37
controllers/page.go Normal file
View file

@ -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)
}

30
controllers/router.go Normal file
View file

@ -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]+)$")

View file

@ -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
Have fun

View file

@ -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{})
}

View file

@ -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

View file

@ -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()) {

View file

@ -1,4 +1,4 @@
package model
package models
import "testing"

View file

@ -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

View file

@ -1,4 +1,4 @@
package model
package models
import "strings"

View file

@ -1,4 +1,4 @@
package service
package services
import (
"fmt"

View file

@ -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) {

View file

@ -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 {