package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "strings" "time" ) //Go Best practices: https://github.com/golovers/effective-go type Item struct { ID int64 `json:"id"` Name string `json:"name"` ValueCents int64 `json:"valueCents"` PurchaseCostCents int64 `json:"purchasedCostCents"` Aquired time.Time `json:"aquired"` Description string `json:"description"` Notes string `json:"notes"` SoldForCents int64 `json:"soldForCents"` SoldTo string `json:"soldTo"` Quantity int64 `json:"quantity"` Category Category `json:"category"` Subcategory Subcategory `json:"subcategory"` } func main() { http.HandleFunc("/categories", categories) //Note: seems adding a trailing slash allows routing URLs like /category/ID# //No need for query parameters. http.HandleFunc("/category/", category) http.HandleFunc("/subcategory/", subcategory) err := http.ListenAndServe(":8088", nil) if err != nil { panic(err) } } func categories(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": getCategoriesHandler(w, r) default: w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Method not allowed")) } } func category(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": getCategoryHandler(w, r) case "POST": insertCategoryHandler(w, r) case "DELETE": deleteCategoryHandler(w, r) default: w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Method not allowed")) } } func subcategory(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": insertSubcategoryHandler(w, r) case "DELETE": deleteSubcategoryHandler(w, r) default: w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Method not allowed")) } } func deleteSubcategoryHandler(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/subcategory/") value, err := strconv.ParseInt(id, 0, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } if !deleteSubcategory(value) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Failed to delete %d", value))) return } w.WriteHeader(http.StatusOK) } func getCategoryHandler(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/category/") value, err := strconv.ParseInt(id, 0, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } category, ok := getCategoryByID(value) if !ok { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to get category.")) return } jsonBytes, err := json.Marshal(&category) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } func deleteCategoryHandler(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/category/") value, err := strconv.ParseInt(id, 0, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } if !deleteCategory(value) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Failed to delete %d", value))) return } w.WriteHeader(http.StatusOK) } func getCategoriesHandler(w http.ResponseWriter, r *http.Request) { categories, ok := getAllCategories() if !ok { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to fetch a list of categories.")) return } jsonBytes, err := json.Marshal(categories) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to get a list of categories.")) return } w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } func insertCategoryHandler(w http.ResponseWriter, r *http.Request) { ct := r.Header.Get("content-type") if ct != "application/json" { w.WriteHeader(http.StatusUnsupportedMediaType) w.Write([]byte(fmt.Sprintf("Unsupported media type '%s', expected application/json", ct))) return } bodyBytes, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to parse request body")) return } defer r.Body.Close() var category Category err = json.Unmarshal(bodyBytes, &category) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to unmarshal JSON")) return } id, ok := insertNewCategory(category.Name, category.Subcategories) if !ok { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to create the new category.")) return } w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("New Record: %d", id))) } func insertSubcategoryHandler(w http.ResponseWriter, r *http.Request) { ct := r.Header.Get("content-type") if ct != "application/json" { w.WriteHeader(http.StatusUnsupportedMediaType) w.Write([]byte(fmt.Sprintf("Unsupported media type '%s', expected application/json", ct))) return } bodyBytes, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to parse request body")) return } defer r.Body.Close() var subcategory Subcategory err = json.Unmarshal(bodyBytes, &subcategory) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Failed to unmarshal JSON")) return } id, ok := insertSubcategory(subcategory.Name, subcategory.CategoryId) if !ok { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to create the new subcategory.")) return } w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf("New Record: %d", id))) }