Archived
1
0
Fork 0
This repository has been archived on 2024-12-18. You can view files and clone it, but cannot push or open issues or pull requests.
restockbot/api.go
Julien Riou 7c2c1032cb
Make the linter happy
Signed-off-by: Julien Riou <julien@riou.xyz>
2021-04-01 17:57:17 +02:00

169 lines
4.5 KiB
Go

package main
import (
"fmt"
"net/http"
"strconv"
"time"
"encoding/json"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
)
// StatusWriter to log response status code
type StatusWriter struct {
http.ResponseWriter
Status int
}
// NewStatusResponseWriter to create a new StatusWriter handler for HTTP logging
func NewStatusResponseWriter(w http.ResponseWriter) *StatusWriter {
return &StatusWriter{
ResponseWriter: w,
Status: http.StatusOK,
}
}
// WriteHeader to write status code to response
func (sw *StatusWriter) WriteHeader(code int) {
sw.Status = code
sw.ResponseWriter.WriteHeader(code)
}
// LoggingMiddleware to log HTTP requests
func LoggingMiddleware(r *mux.Router) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
sw := NewStatusResponseWriter(w)
defer func() {
log.Printf("%s %s %v %d %s %s", req.RemoteAddr, req.Method, time.Since(start), sw.Status, req.URL.Path, req.URL.RawQuery)
}()
next.ServeHTTP(w, req)
})
}
}
// handle health checks
func handleHealth(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "OK")
}
// shopsHandler to expose shops over HTTP with a database connection
type shopsHandler struct {
db *gorm.DB
}
// ServeHTTP to implement the handle interface for serving shops
func (h *shopsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var shops []Shop
trx := h.db.Find(&shops)
if trx.Error == nil {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(shops)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}
// shopHandler to expose shops over HTTP with a database connection
type shopHandler struct {
db *gorm.DB
}
// ServeHTTP to implement the handle interface for serving shop
func (h *shopHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
id := vars["id"]
var shop Shop
trx := h.db.First(&shop, id)
if trx.Error == nil {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(shop)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}
// productsHandler to expose products over HTTP with a database connection
type productsHandler struct {
db *gorm.DB
}
// ServeHTTP to implement the handle interface for serving products
func (h *productsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var products []Product
var trx *gorm.DB
availableFilter := r.URL.Query().Get("available")
if availableFilter != "" {
available, err := strconv.ParseBool(availableFilter)
if err != nil {
log.Warnf("cannot parse available query to boolean: %s", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
trx = h.db.Preload("Shop").Where(map[string]interface{}{"available": available}).Find(&products)
} else {
trx = h.db.Preload("Shop").Find(&products)
}
if trx.Error == nil {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(products)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}
// productHandler to expose product over HTTP with a database connection
type productHandler struct {
db *gorm.DB
}
// ServeHTTP to implement the handle interface for serving product
func (h *productHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
var product Product
trx := h.db.Preload("Shop").First(&product, id)
if trx.Error == nil {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(product)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
}
// StartAPI to handle HTTP requests
func StartAPI(db *gorm.DB, config APIConfig) error {
router := mux.NewRouter().StrictSlash(true)
router.Path("/health").HandlerFunc(handleHealth)
router.Path("/shops").Handler(&shopsHandler{db: db})
router.Path("/shops/{id:[0-9]+}").Handler(&shopHandler{db: db})
router.Path("/products").Handler(&productsHandler{db: db})
router.Path("/products/{id:[0-9]+}").Handler(&productHandler{db: db})
// register middlewares
router.Use(LoggingMiddleware(router))
log.Printf("starting API on %s", config.Address)
if config.Certfile != "" && config.Keyfile != "" {
return http.ListenAndServeTLS(config.Address, config.Certfile, config.Keyfile, router)
}
return http.ListenAndServe(config.Address, router)
}