commit 7d6ca2d8ebc92d0b21cd51d1813a70cfa705c2c2 Author: Ruidy Nemausat Date: Mon Jul 13 23:21:30 2020 +0200 get all products diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..442ccf7 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/rjNemo/go-micro + +go 1.14 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7bb54b8 --- /dev/null +++ b/go.sum @@ -0,0 +1 @@ +golang.org/x/tools v0.0.0-20200713195033-f8240f79c3d3 h1:xV8QVipADSbgfDrrjnUyOJILDkpbFpyoLV4x06POJ7I= diff --git a/handlers/products.go b/handlers/products.go new file mode 100644 index 0000000..e8e02b0 --- /dev/null +++ b/handlers/products.go @@ -0,0 +1,27 @@ +package handlers + +import ( + "fmt" + "log" + "net/http" + + "github.com/rjNemo/go-micro/products/data" +) + +// Products is a handler for Products API service +type Products struct { + logger *log.Logger +} + +// NewProducts creates a Products handler +func NewProducts(logger *log.Logger) *Products { return &Products{logger: logger} } + +func (p *Products) ServeHTTP(w http.ResponseWriter, r *http.Request) { + productList := data.AllProducts() + err := productList.ToJSON(w) + if err != nil { + errMsg := fmt.Sprintf("Unable to encode request: %s\n", err) + http.Error(w, errMsg, http.StatusInternalServerError) + return + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..ebb0fd5 --- /dev/null +++ b/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "time" + + "github.com/rjNemo/go-micro/handlers" + "github.com/rjNemo/go-micro/server" +) + +const port = ":5000" + +func main() { + logger := log.New(os.Stdout, "Product API: ", log.LstdFlags|log.Lshortfile) + + // create the handlers + productsHandler := handlers.NewProducts(logger) + // create a server mux and register the handlers + mux := http.NewServeMux() + mux.Handle("/", productsHandler) + + // creates a new server + srv := server.New(mux, port) + + // non blocking application server + go func() { + logger.Printf("Server started at address http://localhost%s...", port) + logger.Fatalf("Server failed: %v", srv.ListenAndServe()) + }() + + // catch sigterm or interrupt and gracefully terminates the server + sigChan := make(chan os.Signal) + signal.Notify(sigChan, os.Interrupt) + signal.Notify(sigChan, os.Kill) + + sig := <-sigChan + logger.Printf("Received %v signal... graceful shutdown", sig) + + toCtx, _ := context.WithTimeout(context.Background(), 30*time.Second) + srv.Shutdown(toCtx) +} diff --git a/products/data/product.go b/products/data/product.go new file mode 100644 index 0000000..26bae00 --- /dev/null +++ b/products/data/product.go @@ -0,0 +1,55 @@ +package data + +import ( + "encoding/json" + "io" + "time" +) + +// Product defines the structure of a product +type Product struct { + ID int `json:"id"` //TODO: use uuid + Name string `json:"name"` + Description string `json:"description"` + Price float32 `json:"price"` // TODO: use int + SKU string `json:"sku"` + CreatedOn string `json:"-"` + UpdatedOn string `json:"-"` + DeletedOn string `json:"-"` +} + +// Products is the collection of products. +// It encapsulates data access logic +type Products []*Product + +// All returns all existing products +func AllProducts() Products { + return productList +} + +// ToJSON returns all existing product in JSON format +func (p *Products) ToJSON(w io.Writer) error { + return json.NewEncoder(w).Encode(p) // more efficient in memory and time than Marshal +} + +// dummy persistence layer +var productList = []*Product{ + { + ID: 1, + Name: "Latte", + Description: "Prothy Milky Coffee", + Price: 2.45, + SKU: "abc123", + CreatedOn: time.Now().String(), + UpdatedOn: time.Now().String(), + }, + { + ID: 2, + Name: "Espresso", + Description: "Short Strong Coffee without Milk", + Price: 1.99, + SKU: "efg456", + CreatedOn: time.Now().String(), + UpdatedOn: time.Now().String(), + }, +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..eb275a2 --- /dev/null +++ b/server/server.go @@ -0,0 +1,17 @@ +package server + +import ( + "net/http" + "time" +) + +// New creates a server using given mux and port +func New(mux *http.ServeMux, port string) *http.Server { + return &http.Server{ + Addr: port, + Handler: mux, + IdleTimeout: 120 * time.Second, // keep connection opened to prevent Ddos attacks + ReadTimeout: 1 * time.Second, + WriteTimeout: 1 * time.Second, + } +}