package api import ( "time" "myapp/cache" "myapp/entity" "myapp/service" "myapp/sql" "github.com/kataras/iris/v12" ) // ProductHandler is the http mux for products. type ProductHandler struct { service *service.ProductService cache *cache.Cache } // NewProductHandler returns the main controller for the products API. func NewProductHandler(service *service.ProductService) *ProductHandler { return &ProductHandler{ service: service, cache: cache.New(service, "products", time.Minute), } } // GetByID fetches a single record from the database and sends it to the client. // Method: GET. func (h *ProductHandler) GetByID(ctx iris.Context) { id := ctx.Params().GetString("id") var product []byte err := h.cache.GetByID(ctx.Request().Context(), id, &product) if err != nil { if err == sql.ErrNoRows { writeEntityNotFound(ctx) return } debugf("ProductHandler.GetByID(id=%v): %v", id, err) writeInternalServerError(ctx) return } ctx.ContentType("application/json") ctx.Write(product) // ^ Could use our simple `noCache` or implement a Cache-Control (see kataras/iris/cache for that) // but let's keep it simple. } // List lists a set of records from the database. // Method: GET. func (h *ProductHandler) List(ctx iris.Context) { key := ctx.Request().URL.RawQuery products := []byte("[]") err := h.cache.List(ctx.Request().Context(), key, &products) if err != nil && err != sql.ErrNoRows { debugf("ProductHandler.List(DB) (%s): %v", key, err) writeInternalServerError(ctx) return } ctx.ContentType("application/json") ctx.Write(products) } // Create adds a record to the database. // Method: POST. func (h *ProductHandler) Create(ctx iris.Context) { var product entity.Product if err := ctx.ReadJSON(&product); err != nil { return } id, err := h.service.Insert(ctx.Request().Context(), product) if err != nil { if err == sql.ErrUnprocessable { ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) return } debugf("ProductHandler.Create(DB): %v", err) writeInternalServerError(ctx) return } // Send 201 with body of {"id":$last_inserted_id"}. ctx.StatusCode(iris.StatusCreated) ctx.JSON(iris.Map{product.PrimaryKey(): id}) } // Update performs a full-update of a record in the database. // Method: PUT. func (h *ProductHandler) Update(ctx iris.Context) { var product entity.Product if err := ctx.ReadJSON(&product); err != nil { return } affected, err := h.service.Update(ctx.Request().Context(), product) if err != nil { if err == sql.ErrUnprocessable { ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) return } debugf("ProductHandler.Update(DB): %v", err) writeInternalServerError(ctx) return } status := iris.StatusOK if affected == 0 { status = iris.StatusNotModified } ctx.StatusCode(status) } // PartialUpdate is the handler for partially update one or more fields of the record. // Method: PATCH. func (h *ProductHandler) PartialUpdate(ctx iris.Context) { id := ctx.Params().GetInt64Default("id", 0) var attrs map[string]interface{} if err := ctx.ReadJSON(&attrs); err != nil { return } affected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs) if err != nil { if err == sql.ErrUnprocessable { ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "unsupported value(s)")) return } debugf("ProductHandler.PartialUpdate(DB): %v", err) writeInternalServerError(ctx) return } status := iris.StatusOK if affected == 0 { status = iris.StatusNotModified } ctx.StatusCode(status) } // Delete removes a record from the database. // Method: DELETE. func (h *ProductHandler) Delete(ctx iris.Context) { id := ctx.Params().GetInt64Default("id", 0) affected, err := h.service.DeleteByID(ctx.Request().Context(), id) if err != nil { debugf("ProductHandler.Delete(DB): %v", err) writeInternalServerError(ctx) return } status := iris.StatusOK // StatusNoContent if affected == 0 { status = iris.StatusNotModified } ctx.StatusCode(status) }