initial commit
Change-Id: Ied1b5f2137f2b5f803d5411235607bd27c5e57ba
This commit is contained in:
163
main.go
Normal file
163
main.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Domain model
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type Item struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ItemRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// In-memory store
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
store sync.Map
|
||||
counter atomic.Int64
|
||||
)
|
||||
|
||||
func nextID() int {
|
||||
return int(counter.Add(1))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
func itemIDFromPath(path string) (int, bool) {
|
||||
parts := strings.Split(strings.TrimSuffix(path, "/"), "/")
|
||||
if len(parts) < 3 {
|
||||
return 0, false
|
||||
}
|
||||
id, err := strconv.Atoi(parts[len(parts)-1])
|
||||
return id, err == nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func healthHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "UP"})
|
||||
}
|
||||
|
||||
func itemsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Route: /api/items or /api/items/{id}
|
||||
hasSub := strings.Count(strings.TrimSuffix(r.URL.Path, "/"), "/") >= 3
|
||||
|
||||
if hasSub {
|
||||
id, ok := itemIDFromPath(r.URL.Path)
|
||||
if !ok {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
|
||||
return
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
v, exists := store.Load(id)
|
||||
if !exists {
|
||||
writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, v)
|
||||
case http.MethodPut:
|
||||
var req ItemRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
existingRaw, exists := store.Load(id)
|
||||
if !exists {
|
||||
writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
|
||||
return
|
||||
}
|
||||
existing := existingRaw.(Item)
|
||||
if req.Name == "" {
|
||||
req.Name = existing.Name
|
||||
}
|
||||
if req.Description == "" {
|
||||
req.Description = existing.Description
|
||||
}
|
||||
updated := Item{ID: id, Name: req.Name, Description: req.Description}
|
||||
store.Store(id, updated)
|
||||
writeJSON(w, http.StatusOK, updated)
|
||||
case http.MethodDelete:
|
||||
store.Delete(id)
|
||||
writeJSON(w, http.StatusOK, map[string]int{"deleted": id})
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// /api/items — list or create
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
var items []Item
|
||||
store.Range(func(_, v any) bool {
|
||||
items = append(items, v.(Item))
|
||||
return true
|
||||
})
|
||||
if items == nil {
|
||||
items = []Item{}
|
||||
}
|
||||
writeJSON(w, http.StatusOK, items)
|
||||
case http.MethodPost:
|
||||
var req ItemRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
req.Name = "unnamed"
|
||||
}
|
||||
item := Item{ID: nextID(), Name: req.Name, Description: req.Description}
|
||||
store.Store(item.ID, item)
|
||||
writeJSON(w, http.StatusCreated, item)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/health", healthHandler)
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
mux.HandleFunc("/api/items/", itemsHandler)
|
||||
mux.HandleFunc("/api/items", itemsHandler)
|
||||
|
||||
addr := ":8080"
|
||||
fmt.Printf("tmpl-test-go listening on %s\n", addr)
|
||||
log.Fatal(http.ListenAndServe(addr, mux))
|
||||
}
|
||||
Reference in New Issue
Block a user