Skip to content

Commit

Permalink
chore(allsrv): add implementation of allsrv server
Browse files Browse the repository at this point in the history
This server represents a server with minimum abstraction. It takes the
popular convention from the Matt Ryer post and implements it in an intensely
terse manner.

Explore this allsrv pkg and answer the following questions:

Question: What stands out?
Question: What do you find favorable?
Question: What do you find nasueating?

Refs: [How I write HTTP Services After 8 Years - Matt Ryer](https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html)
  • Loading branch information
jsteenb2 committed Jul 5, 2024
0 parents commit 643f58a
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
166 changes: 166 additions & 0 deletions allsrv/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package allsrv

import (
"encoding/json"
"errors"
"log"
"net/http"

"github.com/gofrs/uuid"
)

type Server struct {
db *inmemDB

user, pass string
}

func NewServer(db *inmemDB, user, pass string) *Server {
s := Server{
db: db,
user: user,
pass: pass,
}
s.routes()
return &s
}

func (s *Server) routes() {
http.Handle("POST /foo", http.HandlerFunc(s.createFoo))
http.Handle("GET /foo", http.HandlerFunc(s.readFoo))
http.Handle("PUT /foo", http.HandlerFunc(s.updateFoo))
http.Handle("DELETE /foo", http.HandlerFunc(s.delFoo))
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.DefaultServeMux.ServeHTTP(w, r)
}

type Foo struct {
ID string `json:"id" gorm:"id"`
Name string `json:"name" gorm:"name"`
Note string `json:"note" gorm:"note"`
}

func (s *Server) createFoo(w http.ResponseWriter, r *http.Request) {
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
w.WriteHeader(http.StatusUnauthorized)
return
}

var f Foo
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
w.WriteHeader(http.StatusForbidden)
return
}

newFooID, err := s.db.createFoo(f)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

f, err = s.db.readFoo(newFooID)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}

w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(f); err != nil {
log.Printf("unexpected error writing json value to response body: " + err.Error())
}
}

func (s *Server) readFoo(w http.ResponseWriter, r *http.Request) {
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
w.WriteHeader(http.StatusUnauthorized)
return
}

f, err := s.db.readFoo(r.URL.Query().Get("id"))
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}

if err := json.NewEncoder(w).Encode(f); err != nil {
log.Printf("unexpected error writing json value to response body: " + err.Error())
}
}

func (s *Server) updateFoo(w http.ResponseWriter, r *http.Request) {
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
w.WriteHeader(http.StatusUnauthorized)
return
}

var f Foo
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
w.WriteHeader(http.StatusForbidden)
return
}

if err := s.db.updateFoo(f); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}

func (s *Server) delFoo(w http.ResponseWriter, r *http.Request) {
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
w.WriteHeader(http.StatusUnauthorized)
return
}

if err := s.db.delFoo(r.URL.Query().Get("id")); err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
}

type inmemDB struct {
m []Foo
}

func (db *inmemDB) createFoo(f Foo) (string, error) {
f.ID = uuid.Must(uuid.NewV4()).String()

for _, existing := range db.m {
if f.Name == existing.Name {
return "", errors.New("foo " + f.Name + " exists")
}
}

db.m = append(db.m, f)

return f.ID, nil
}

func (db *inmemDB) readFoo(id string) (Foo, error) {
for _, f := range db.m {
if id == f.ID {
return f, nil
}
}
return Foo{}, errors.New("foo not found for id: " + id)
}

func (db *inmemDB) updateFoo(f Foo) error {
for i, existing := range db.m {
if f.ID == existing.ID {
db.m[i] = f
return nil
}
}
return errors.New("foo not found for id: " + f.ID)
}

func (db *inmemDB) delFoo(id string) error {
for i, f := range db.m {
if id == f.ID {
db.m = append(db.m[:i], db.m[i+1:]...)
}
}
return errors.New("foo not found for id: " + id)
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module mess

go 1.22

require github.com/gofrs/uuid v4.4.0+incompatible
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=

0 comments on commit 643f58a

Please sign in to comment.