1b4b0a6acccf05dc669c1b38046c201761bcf229 — HokieGeek 24 days ago 87e0f08 master v8.0.0
Added progenitor field and did some refactoring
4 files changed, 173 insertions(+), 72 deletions(-)

A cmd/dupefinder/main.go
M cmd/teadbd/main.go
M cmd/teas/main.go
M gcp.go
A cmd/dupefinder/main.go => cmd/dupefinder/main.go +83 -0
@@ 0,0 1,83 @@
+ package main
+ 
+ import (
+ 	"flag"
+ 	"fmt"
+ 	"strings"
+ 
+ 	"git.sr.ht/~hokiegeek/teadb"
+ )
+ 
+ func main() {
+ 	projectIDPtr := flag.String("project", "", "Need to know the project name")
+ 	flag.Parse()
+ 
+ 	db, err := teadb.New(*projectIDPtr)
+ 	if err != nil {
+ 		panic(err)
+ 	}
+ 
+ 	teas, err := db.AllTeas()
+ 	if err != nil {
+ 		panic(err)
+ 	}
+ 
+ 	candidates := make(map[string][]teadb.Tea)
+ 	for _, t := range teas {
+ 		k := fmt.Sprintf("%s %s %s %d", strings.ToLower(t.Purchaselocation), strings.ToLower(t.Type), strings.ToLower(t.Name), t.Year)
+ 		if _, ok := candidates[k]; !ok {
+ 			candidates[k] = make([]teadb.Tea, 0)
+ 		}
+ 		candidates[k] = append(candidates[k], t)
+ 	}
+ 
+ 	type caught struct {
+ 		tea teadb.Tea
+ 		err error
+ 	}
+ 	catcher := make([]caught, 0)
+ 
+ 	for k, teas := range candidates {
+ 		switch {
+ 		case len(teas) == 1:
+ 			teas[0].Progenitor = -1
+ 			if err := db.UpdateTea(teas[0]); err != nil {
+ 				catcher = append(catcher, caught{teas[0], err})
+ 			}
+ 		case len(teas) > 1:
+ 			fmt.Println(k)
+ 			parent := teas[0].ID
+ 			for _, tea := range teas {
+ 				if tea.ID <= parent {
+ 					parent = tea.ID
+ 				}
+ 			}
+ 
+ 			for _, tea := range teas {
+ 				switch {
+ 				case tea.ID == parent:
+ 					tea.Progenitor = -1
+ 					fmt.Printf("\t%d> %s (parent)\n", tea.ID, tea.Name)
+ 				default:
+ 					tea.Progenitor = parent
+ 					fmt.Printf("\t%d> %s (%d)\n", tea.ID, tea.Name, parent)
+ 				}
+ 				if err := db.UpdateTea(tea); err != nil {
+ 					catcher = append(catcher, caught{tea, err})
+ 				}
+ 			}
+ 		}
+ 	}
+ 
+ 	/*
+ 		for _, teas := range candidates {
+ 			if len(teas) == 1 {
+ 				fmt.Printf("ORPHAN? %d> %s\n", teas[0].ID, teas[0].Name)
+ 			}
+ 		}
+ 	*/
+ 
+ 	for _, c := range catcher {
+ 		fmt.Printf("error: could not resolve (%d) %s: %v\n", c.tea.ID, c.tea.Name, c.err)
+ 	}
+ }

M cmd/teadbd/main.go => cmd/teadbd/main.go +65 -42
@@ 61,7 61,7 @@ exposedOk := handlers.ExposedHeaders([]string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "Etag"})
  	methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"})
  
- 	http.ListenAndServe(fmt.Sprintf(":%d", *portPtr), handlers.CORS(originsOk, headersOk, methodsOk, exposedOk)(r))
+ 	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *portPtr), handlers.CORS(originsOk, headersOk, methodsOk, exposedOk)(r)))
  }
  
  func getAllTeasHandler(w http.ResponseWriter, r *http.Request, db *teadb.GcpClient, cache *teadb.Cache) {


@@ 122,6 122,7 @@   	switch r.Method {
  	case http.MethodHead:
+ 		return
  	case http.MethodGet:
  		tea, valid := cache.Tea(id)
  		if !valid {


@@ 141,40 142,54 @@ w.WriteHeader(http.StatusConflict)
  			return
  		}
- 		if tea, err := readTea(); err == nil {
- 			if err = db.CreateTea(tea); err != nil {
- 				w.WriteHeader(http.StatusInternalServerError)
- 			} else {
- 				cache.Invalidate()
- 				w.WriteHeader(http.StatusCreated)
- 			}
+ 
+ 		tea, err := readTea()
+ 		if err != nil {
+ 			http.Error(w, err.Error(), http.StatusBadRequest)
+ 			return
+ 		}
+ 
+ 		if err = db.CreateTea(tea); err != nil {
+ 			w.WriteHeader(http.StatusInternalServerError)
+ 			return
  		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusCreated)
  	case http.MethodPut:
  		// Update existing TEA
  		if _, err := db.TeaByID(id); err != nil {
  			w.WriteHeader(http.StatusNotFound)
  			return
  		}
- 		if tea, err := readTea(); err == nil {
- 			if err = db.UpdateTea(tea); err != nil {
- 				w.WriteHeader(http.StatusInternalServerError)
- 			} else {
- 				cache.Invalidate()
- 				w.WriteHeader(http.StatusOK)
- 			}
+ 
+ 		tea, err := readTea()
+ 		if err != nil {
+ 			http.Error(w, err.Error(), http.StatusBadRequest)
+ 			return
  		}
+ 
+ 		if err = db.UpdateTea(tea); err != nil {
+ 			w.WriteHeader(http.StatusInternalServerError)
+ 			return
+ 		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusOK)
  	case http.MethodDelete:
  		// Delete existing TEA
  		if _, err := db.TeaByID(id); err != nil {
  			w.WriteHeader(http.StatusNotFound)
  			return
  		}
+ 
  		if err = db.DeleteTea(id); err != nil {
  			w.WriteHeader(http.StatusInternalServerError)
- 		} else {
- 			cache.Invalidate()
- 			w.WriteHeader(http.StatusOK)
+ 			return
  		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusOK)
  	}
  }
  


@@ 229,6 244,7 @@   	switch r.Method {
  	case http.MethodHead:
+ 		return
  	case http.MethodGet:
  		for _, teaEntry := range tea.Entries {
  			if entryid == teaEntry.Datetime.UnixNano() {


@@ 238,24 254,34 @@ }
  		w.WriteHeader(http.StatusNotFound)
  	case http.MethodPost:
- 		if entry, err := readEntry(); err == nil {
- 			if err = db.CreateEntry(tea.ID, entry); err != nil {
- 				http.Error(w, "error creating new entry", http.StatusInternalServerError)
- 			} else {
- 				cache.Invalidate()
- 				w.WriteHeader(http.StatusCreated)
- 			}
+ 		entry, err := readEntry()
+ 		if err != nil {
+ 			http.Error(w, err.Error(), http.StatusBadRequest)
+ 			return
  		}
+ 
+ 		if err = db.CreateEntry(tea.ID, entry); err != nil {
+ 			http.Error(w, "error creating new entry", http.StatusInternalServerError)
+ 			return
+ 		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusCreated)
  	case http.MethodPut:
  		// Update existing tea entry
- 		if entry, err := readEntry(); err == nil {
- 			if err = db.UpdateEntry(tea.ID, entry); err != nil {
- 				http.Error(w, "error updating entry", http.StatusInternalServerError)
- 			} else {
- 				cache.Invalidate()
- 				w.WriteHeader(http.StatusOK)
- 			}
+ 		entry, err := readEntry()
+ 		if err != nil {
+ 			http.Error(w, err.Error(), http.StatusBadRequest)
+ 			return
+ 		}
+ 
+ 		if err = db.UpdateEntry(tea.ID, entry); err != nil {
+ 			http.Error(w, "error updating entry", http.StatusInternalServerError)
+ 			return
  		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusOK)
  	case http.MethodDelete:
  		for i, teaEntry := range tea.Entries {
  			if entryid == teaEntry.Datetime.Unix() {


@@ 266,22 292,24 @@   		if err = db.UpdateTea(tea); err != nil {
  			w.WriteHeader(http.StatusInternalServerError)
- 		} else {
- 			cache.Invalidate()
- 			w.WriteHeader(http.StatusOK)
+ 			return
  		}
+ 
+ 		cache.Invalidate()
+ 		w.WriteHeader(http.StatusOK)
  	}
  }
  
  func postJSON(w http.ResponseWriter, r *http.Request, payload interface{}) {
- 	JSON, err := json.Marshal(payload)
+ 	buf, err := json.Marshal(payload)
  	if err != nil {
  		w.WriteHeader(http.StatusInternalServerError)
  		return
  	}
  
+ 	hash := md5.Sum(buf)
+ 	sum := hex.EncodeToString(hash[:])
  	requestedSum := r.Header.Get("If-None-Match")
- 	sum := checksum(JSON)
  	if requestedSum == sum {
  		w.WriteHeader(http.StatusNotModified)
  		return


@@ 300,8 328,3 @@ http.Error(w, "error sending payload", http.StatusInternalServerError)
  	}
  }
- 
- func checksum(body []byte) string {
- 	hash := md5.Sum(body)
- 	return hex.EncodeToString(hash[:])
- }

M cmd/teas/main.go => cmd/teas/main.go +18 -17
@@ 6,23 6,17 @@ "fmt"
  	"io/ioutil"
  	"log"
- 	"os"
  
  	"git.sr.ht/~hokiegeek/teadb"
  )
  
  func main() {
- 	// Command flags: load
- 	loadCommand := flag.NewFlagSet("load", flag.ExitOnError)
- 	loadFilePtr := loadCommand.String("file", "", "The filename to use")
- 
- 	// Command flags: save
- 	saveCommand := flag.NewFlagSet("save", flag.ExitOnError)
- 	saveFilePtr := saveCommand.String("file", "", "The filename to use")
- 
+ 	filePtr := flag.String("file", "", "The filename to use")
  	projectIDPtr := flag.String("project", "", "Need to know the project name")
  
- 	command := os.Args[1]
+ 	flag.Parse()
+ 
+ 	command := flag.Arg(0)
  
  	db, err := teadb.New(*projectIDPtr)
  	if err != nil {


@@ 31,13 25,11 @@   	switch command {
  	case "load":
- 		loadCommand.Parse(os.Args[2:])
- 		if err := loadFromJSON(*loadFilePtr, db); err != nil {
+ 		if err := loadFromJSON(*filePtr, db); err != nil {
  			panic(err)
  		}
  	case "save":
- 		saveCommand.Parse(os.Args[2:])
- 		if err := saveToFile(*saveFilePtr, db); err != nil {
+ 		if err := saveToFile(*filePtr, db); err != nil {
  			panic(err)
  		}
  	case "purge":


@@ 61,12 53,21 @@ }
  
  func createTeas(teas []teadb.Tea, db *teadb.GcpClient) error {
+ 	type caught struct {
+ 		tea teadb.Tea
+ 		err error
+ 	}
+ 	catcher := make([]caught, 0)
  	for _, tea := range teas {
  		if err := db.CreateTea(tea); err != nil {
- 			fmt.Printf("Could not create tea %d: %v\n", tea.ID, err)
- 		} else {
- 			fmt.Printf("Tea (%d): %s\n", tea.ID, tea.Name)
+ 			catcher = append(catcher, caught{tea, err})
+ 			continue
  		}
+ 		fmt.Printf("Tea (%d): %s\n", tea.ID, tea.Name)
+ 	}
+ 
+ 	for _, c := range catcher {
+ 		fmt.Printf("error: could not create tea (%d) %s: %v\n", c.tea.ID, c.tea.Name, c.err)
  	}
  
  	return nil

M gcp.go => gcp.go +7 -13
@@ 53,6 53,7 @@ Packaging        string     `json:"packaging"`
  	Sample           bool       `json:"sample"`
  	Entries          []TeaEntry `json:"entries"`
+ 	Progenitor       int        `json:"progenitor"`
  }
  
  // GcpClient is the client struct


@@ 67,20 68,22 @@   // CreateTea creates a new tea entity
  func (c *GcpClient) CreateTea(tea Tea) error {
- 	// TODO: validate?
  	return c.saveTea(tea)
  }
  
  // UpdateTea updates a new tea entity
  func (c *GcpClient) UpdateTea(tea Tea) error {
- 	// TODO: validate?
  	return c.saveTea(tea)
  }
  
  // DeleteTea deletes an existing tea
  func (c *GcpClient) DeleteTea(teaID int) error {
- 	// TODO: validate?
- 	return c.removeTea(teaID)
+ 	// Saves the new entity.
+ 	if err := c.client.Delete(c.ctx, teaKey(teaID)); err != nil {
+ 		return fmt.Errorf("failed to remove tea: %v", err)
+ 	}
+ 
+ 	return nil
  }
  
  // CreateEntry creates a new entry on an existing tea


@@ 159,15 162,6 @@ return nil
  }
  
- func (c *GcpClient) removeTea(teaID int) error {
- 	// Saves the new entity.
- 	if err := c.client.Delete(c.ctx, teaKey(teaID)); err != nil {
- 		return fmt.Errorf("failed to remove tea: %v", err)
- 	}
- 
- 	return nil
- }
- 
  // New creates a new GcpClient
  func New(projectID string) (c *GcpClient, err error) {
  	c = new(GcpClient)