Delivering Orders

Order retrieval

We’ll implement the retrieval endpoint inside the setupRouter() function. Based on the ID passed as URL parameter, we call the retrieveOrder() function to do the actual work and respond accordingly.

r.GET("/retrieve-order/:ID", func(c *gin.Context) {
    ID := c.Param("ID")

    result, success, systemHTTPStatusCode, systemStatusMessage := retrieveOrder(ID)

    if !success {
        fmt.Println("Error retrieving order " + ID + ": " + systemStatusMessage)
        c.JSON(systemHTTPStatusCode, gin.H{"message": systemStatusMessage})
    } else {
        fmt.Println("Order " + ID + " retrieved")
        c.JSON(http.StatusOK, result)
    }

})

The actual logic is rather straightforward. Based on the order ID we check whether the order exists and make sure it isn’t already delivered. If neither of those cases is true but enough coffees have been brewed, we deliver it. Otherwise, something isn’t done just yet.

func retrieveOrder(id string) (*order, bool, int, string) {

	var thisOrder = order{ID: id}
	result := db.Limit(1).Find(&thisOrder)

	if !(result.RowsAffected == 1) {
		return nil, false, http.StatusNotFound, "Order not found!"
	}

	if !(thisOrder.OrderRetrieved.IsZero()) {
		return nil, false, http.StatusGone, "Order already delivered"
	}

	if thisOrder.OrderSize == thisOrder.OrderBrewed {
		thisOrder.OrderRetrieved = time.Now().UTC()
		db.Save(&thisOrder)
		return &thisOrder, true, http.StatusOK, "Order delivered"
	}

	return nil, false, http.StatusServiceUnavailable, "Order not ready"
}

Insights

We also provide some statistical information. One is the order overview with the endpoint

r.GET("/order-list", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"data": listOrders()})
})

implemented by

func listOrders() []order {
    var orderList = []order{}
	db.Find(&orderList)
	return orderList
}

Finally, we’ll also expose some metrics for Prometheus as an API endpoint. This will be the totals of orders_received, orders_ready, orders_retrieved, and job_queue_length.

r.GET("/metrics", func(c *gin.Context) {
    ordersReceivedInt, ordersReadyInt, ordersRetrievedInt, jobQueueLengthInt := getStats()
    ordersReceivedString := "# HELP orders_received The numbers of orders received by the system\n# TYPE orders_received counter\norders_received " + strconv.Itoa(ordersReceivedInt)
    ordersReadyString := "# HELP orders_ready The numbers of orders the system has finished\n# TYPE orders_ready counter\norders_ready " + strconv.Itoa(ordersReadyInt)
    ordersRetrievedString := "# HELP orders_retrieved The numbers of orders retrieved from the system\n# TYPE orders_retrieved counter\norders_retrieved " + strconv.Itoa(ordersRetrievedInt)
    jobQueueLengthString := "# HELP job_queue_length The number of jobs currently in the queue\n#TYPE job_queue_length gauge\njob_queue_length " + strconv.Itoa(jobQueueLengthInt)
    c.String(http.StatusOK, ordersReceivedString+"\n"+ordersReadyString+"\n"+ordersRetrievedString+"\n"+jobQueueLengthString)
})

and counted by

func getStats() (int, int, int, int) {
	var ordersReceivedInt, ordersReadyInt, ordersRetrievedInt, jobQueueLengthInt int64
	db.Model(&order{}).Where("order_received IS NOT NULL").Count(&ordersReceivedInt)
	db.Model(&order{}).Where("order_ready IS NOT NULL").Count(&ordersReadyInt)
	db.Model(&order{}).Where("order_retrieved IS NOT NULL").Count(&ordersRetrievedInt)
	db.Model(&coffeeListItem{}).Where("job_retrieved IS NULL").Count(&jobQueueLengthInt)
	return int(ordersReceivedInt), int(ordersReadyInt), int(ordersRetrievedInt), int(jobQueueLengthInt)
}