mirror of https://github.com/tilde-cat/register
Initial commit
This commit is contained in:
commit
a2c8e2b054
|
@ -0,0 +1,3 @@
|
|||
module github.com/you/hello
|
||||
|
||||
require github.com/satori/go.uuid v1.2.0
|
|
@ -0,0 +1,2 @@
|
|||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
|
@ -0,0 +1,130 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
FormUrl = "/"
|
||||
FormPostUrl = "/post"
|
||||
RequestStatusUrlPrefix = "/status/"
|
||||
ErrorUrl = "/error"
|
||||
)
|
||||
|
||||
var statusRE = regexp.MustCompile(RequestStatusUrlPrefix + `(.+)$`)
|
||||
|
||||
type Id uuid.UUID
|
||||
|
||||
func (id Id) String() string {
|
||||
return uuid.UUID(id).String()
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Username string
|
||||
Email string
|
||||
Why string
|
||||
SSHPublicKey string
|
||||
Status string
|
||||
}
|
||||
|
||||
func (r *Request) IsValid() bool {
|
||||
return r.Username != "" &&
|
||||
r.Email != "" &&
|
||||
r.Why != "" &&
|
||||
r.SSHPublicKey != ""
|
||||
}
|
||||
|
||||
type Io interface {
|
||||
Save(r Request) (Id, error)
|
||||
Load(id Id) (*Request, error)
|
||||
}
|
||||
|
||||
type FsIo struct {
|
||||
}
|
||||
|
||||
func (io *FsIo) Save(r Request) (Id, error) {
|
||||
b, err := json.MarshalIndent(r, "", "\t")
|
||||
if err != nil {
|
||||
return Id{}, err
|
||||
}
|
||||
id := Id(uuid.NewV4())
|
||||
return id, ioutil.WriteFile(id.String()+".json", b, 0600)
|
||||
}
|
||||
|
||||
func (io *FsIo) Load(id Id) (*Request, error) {
|
||||
b, err := ioutil.ReadFile(id.String() + ".json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var req Request
|
||||
if err := json.Unmarshal(b, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Io Io
|
||||
}
|
||||
|
||||
func (s *Server) RequestPage(w http.ResponseWriter, r *http.Request) {
|
||||
m := statusRE.FindStringSubmatch(r.URL.String())
|
||||
if len(m) != 2 {
|
||||
http.Error(w, "missing request id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
uid := m[1]
|
||||
id, err := uuid.FromString(uid)
|
||||
if err != nil {
|
||||
http.Error(w, "no such request: '"+uid+"'", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
req, err := s.Io.Load(Id(id))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "Status: %v", req.Status)
|
||||
}
|
||||
|
||||
func (s *Server) IncorrectRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) FormPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
req := Request{}
|
||||
req.Username = r.PostFormValue("username")
|
||||
req.Email = r.PostFormValue("email")
|
||||
req.Why = r.PostFormValue("why")
|
||||
req.SSHPublicKey = r.PostFormValue("sshpublickey")
|
||||
req.Status = "Pending"
|
||||
if !req.IsValid() {
|
||||
log.Println("Invalid request", r.PostForm)
|
||||
http.Redirect(w, r, ErrorUrl, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
id, err := s.Io.Save(req)
|
||||
log.Println("Valid request", r.PostForm, err)
|
||||
http.Redirect(w, r, RequestStatusUrlPrefix+id.String(), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *Server) FormPage(w http.ResponseWriter, r *http.Request) {
|
||||
formTemplate.Execute(w, nil)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var io FsIo
|
||||
server := Server{Io: &io}
|
||||
http.HandleFunc(RequestStatusUrlPrefix, server.RequestPage)
|
||||
http.HandleFunc(ErrorUrl, server.IncorrectRequest)
|
||||
http.HandleFunc(FormPostUrl, server.FormPostHandler)
|
||||
http.HandleFunc(FormUrl, server.FormPage)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var expected = Request{
|
||||
Username: "name",
|
||||
Email: "test@example.com",
|
||||
Why: "foo bar baz",
|
||||
SSHPublicKey: "123",
|
||||
Status: "Pending",
|
||||
}
|
||||
|
||||
type ReqEntry struct {
|
||||
Request Request
|
||||
Id Id
|
||||
}
|
||||
|
||||
type IoStub struct {
|
||||
Saved []ReqEntry
|
||||
Loads []Id
|
||||
}
|
||||
|
||||
func NewIoStub() *IoStub {
|
||||
return &IoStub{}
|
||||
}
|
||||
|
||||
func (io *IoStub) Save(r Request) (Id, error) {
|
||||
id := Id(uuid.NewV4())
|
||||
io.Saved = append(io.Saved, ReqEntry{r, id})
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (io *IoStub) Load(id Id) (*Request, error) {
|
||||
io.Loads = append(io.Loads, id)
|
||||
for _, r := range io.Saved {
|
||||
if r.Id == id {
|
||||
return &r.Request, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Missing Request for id: %v", id)
|
||||
}
|
||||
|
||||
func requestForm(target string, values map[string]string) *http.Request {
|
||||
r := httptest.NewRequest("POST", target, nil)
|
||||
r.PostForm = url.Values{}
|
||||
for k, v := range values {
|
||||
r.PostForm.Set(k, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestRequestSaveAfterCorrectFormPost(t *testing.T) {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
req := requestForm(FormPostUrl, map[string]string{
|
||||
"username": expected.Username,
|
||||
"email": expected.Email,
|
||||
"why": expected.Why,
|
||||
"sshpublickey": expected.SSHPublicKey,
|
||||
})
|
||||
recorder := httptest.NewRecorder()
|
||||
server.FormPostHandler(recorder, req)
|
||||
resp := recorder.Result()
|
||||
if resp.StatusCode != http.StatusSeeOther {
|
||||
t.Fatalf("Expected status %v, got: %v", http.StatusSeeOther, resp.StatusCode)
|
||||
}
|
||||
expectedLoc := RequestStatusUrlPrefix + io.Saved[0].Id.String()
|
||||
if loc := resp.Header.Get("Location"); loc != expectedLoc {
|
||||
t.Fatalf("Expected location '%v', got '%v'", expectedLoc, loc)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, io.Saved[0].Request) {
|
||||
t.Fatalf("\nExpected '%#v'\n got '%#v'", expected, io.Saved[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedirectToFailureWhenAnyRequestFieldIsEmtpy(t *testing.T) {
|
||||
data := []Request{
|
||||
{Username: "", Email: expected.Email, Why: expected.Why, SSHPublicKey: expected.SSHPublicKey},
|
||||
{Username: expected.Username, Email: "", Why: expected.Why, SSHPublicKey: expected.SSHPublicKey},
|
||||
{Username: expected.Username, Email: expected.Email, Why: "", SSHPublicKey: expected.SSHPublicKey},
|
||||
{Username: expected.Username, Email: expected.Email, Why: expected.Why, SSHPublicKey: ""},
|
||||
}
|
||||
for _, r := range data {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
req := requestForm(FormPostUrl, map[string]string{
|
||||
"username": r.Username,
|
||||
"email": r.Email,
|
||||
"why": r.Why,
|
||||
"sshpublickey": r.SSHPublicKey,
|
||||
})
|
||||
recorder := httptest.NewRecorder()
|
||||
server.FormPostHandler(recorder, req)
|
||||
resp := recorder.Result()
|
||||
if resp.StatusCode != http.StatusSeeOther {
|
||||
t.Fatalf("Expected see other status, got: %v", resp.StatusCode)
|
||||
}
|
||||
if loc := resp.Header.Get("Location"); loc != ErrorUrl {
|
||||
t.Fatalf("Expected location %v, got: %v", ErrorUrl, loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusPageOk(t *testing.T) {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
id, _ := io.Save(expected)
|
||||
req := httptest.NewRequest("GET", RequestStatusUrlPrefix+id.String(), nil)
|
||||
rec := httptest.NewRecorder()
|
||||
server.RequestPage(rec, req)
|
||||
if io.Loads[0] != id {
|
||||
t.Fatalf("Expected load of %v, loaded %v instead", id, io.Loads[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusPageUnknownId(t *testing.T) {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
id := Id(uuid.NewV4())
|
||||
req := httptest.NewRequest("GET", RequestStatusUrlPrefix+id.String(), nil)
|
||||
rec := httptest.NewRecorder()
|
||||
server.RequestPage(rec, req)
|
||||
if io.Loads[0] != id {
|
||||
t.Fatalf("Expected load of %v, loaded %v instead", id, io.Loads[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusPageMalformedId(t *testing.T) {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
id := Id(uuid.NewV4())
|
||||
req := httptest.NewRequest("GET", RequestStatusUrlPrefix+id.String()+"abc", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
server.RequestPage(rec, req)
|
||||
if l := len(io.Loads); l != 0 {
|
||||
t.Fatalf("Expected zero loads, got %v", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusPageMissingId(t *testing.T) {
|
||||
io := NewIoStub()
|
||||
server := Server{Io: io}
|
||||
req := httptest.NewRequest("GET", RequestStatusUrlPrefix, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
server.RequestPage(rec, req)
|
||||
if l := len(io.Loads); l != 0 {
|
||||
t.Fatalf("Expected zero loads, got %v", l)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import "html/template"
|
||||
|
||||
var formTemplate = template.Must(template.New("form").Parse(`
|
||||
<html>
|
||||
<body>
|
||||
<h1>~🐱 signup form</h1>
|
||||
<form action="/post" method="post">
|
||||
Username:
|
||||
<input type="text" name="username"><br/>
|
||||
Email:
|
||||
<input type="email" name="email"><br/>
|
||||
Why would you want an account here?
|
||||
<textarea name="why" cols=50 rows=10></textarea><br/>
|
||||
SSH key:
|
||||
<textarea name="sshpublickey" cols=50 rows=10></textarea><br/>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`))
|
Loading…
Reference in New Issue