Golang shopping cart with REST API and support for rules on discounts, promos, bundles, and comes with Swagger documentations
- Sample Swagger Doc
- It was not used to generate the server side code; only used for testing and documentation
- See cart/static/api/swagger.json
This shopping cart is API driver and also Rules driven though the implementation will be improved further to make the rules outside of the code. More rules will be added or if you have any suggestions, please let me know.
- If you buy 3 items, you pay only two items
- In reality this could come from a database or external config which can be updated anytime
Scenario: A 3 for 2 deal on Unlimited 1GB Sims. So for example, if you buy 3 Unlimited 1GB Sims, you will pay the price of 2 only for the first month.
var Rule_BuyThreePayTwoOnly = map[string]bool{"ult_small":true,
}
- The price will drop to $$ each for the first month, if the customer buys more than x items.
- In reality this could come from a database or external config which can be updated anytime
Scenario: The Unlimited 5GB Sim will have a bulk discount applied; whereby the price will drop to $39.90 each for the first month, if the customer buys more than 3.
var Rule_BulkDiscountMoreThanThree = map[string]float64{"ult_large":39.90,
}
- We will bundle in a free item X free-of-charge with every Y sold
- In reality this could come from a database or external config which can be updated anytime
var Rule_BundleFreeForEveryItemBought = map[string]string{"ult_medium":"1gb",
}
- Adding the promo code X will apply a $$ discount across the board.
- In reality this could come from a database or external config which can be updated anytime
var Rule_PromoCodeDiscount = map[string]float64{"I<3AMAYSIM":10,
}
Below are the handlers which serves as the handlers for REST API calls. These are currently hand-coded 100% but in the future we will use Swagger.JSON to automatically generate some of these server codes.
http.HandleFunc("/", handlerRoot)
http.HandleFunc("/products", handleFuncProducts)
http.HandleFunc("/cart", handleFuncCart)
http.HandleFunc("/process", handleFuncProcess)
These are the major functions used by the shopping cart api.
Handles management of products using POST, GET, DELETE
Handles management of shopping cart using POST, GET, DELETE
Handles pre-checkout and post-checkout processing and does most of the rules engine processing, application of discounts, bundles etc.
The rules engine for Buy Three Pay Two Only rule
func (s *CartProc) Check_Rule_BulkDiscountMoreThanThree(w http.ResponseWriter, r *http.Request) error {}
The rules engine for the Bulk Discount More Than Three rule
func (s *CartProc) Check_Rule_BundleFreeForEveryItemBought(w http.ResponseWriter, r *http.Request) error {}
The rules engine for the Bundle Free for Every Item Bought
func (s *CartProc) Check_Rule_PromoCodeDiscount(w http.ResponseWriter, r *http.Request, promo string) error {}
The rules engine for the Promo Code Discounts
If no rules apply to other cart items, this logic is being applied
The shopping cart api can be deployed to your appengine project in less 10 minutes, provided you have prior experience with GAE. Otherwise, please consult Google Appengine docs first.
- Then, update app.yaml to indicate your new appengine project ID
- Execute the updategae.sh script
cd shopping-cart-api/cart
./updategae.sh
The good thing about shopping-cart-api is ease of testing the API endpoints. The included test scenarios uses HTTP testing tools to automatically call the API such as posting new products, simulating addition of items to cart and then processing the cart as if user is checking out. This is a very nice to have feature for anything that involves API development. Remember, we can get tired of doing CURL executions but if we can bundle these tests into automated tests, life is easier! Good thing Golang has built-in testing tool called "go test" and the https://github.com/gavv/httpexpect testing package also make it easy to call and analyze API responses.
Scenario: A 3 for 2 deal on Unlimited 1GB Sims. So for example, if you buy 3 Unlimited 1GB Sims, you will pay the price of 2 only for the first month.
cd test-cases/cart-test-scenario1
go test -v
//after this command, all tables are refreshed with data
[CLR0001] Test clear the database tables in appengine --> Products
[CLR0002] Test clear the database tables in appengine --> Cart
[ADD0001] Test add product 1 to catalog --> ult_small Unlimited 1GB $24.90
[ADD0002] Test add product 2 to catalog --> ult_medium Unlimited 2GB $29.90
[ADD0003] Test add product 3 to catalog --> ult_large Unlimited 5GB $44.90
[ADD0004] Test add product 4 to catalog --> 1gb 1 GB Data-pack $9.90
[SC10001] Test Add 3 items of ult_small Unlimited 1GB $24.90
[SC10001] Test Add 1 items of ult_large Unlimited 5GB $44.90
PASS
ok github.com/edwindvinas/shopping-cart-api/cart-test-scenario1 3.266s
//execute curl to simulate checkout of cart
$ curl -X GET --header 'Accept: application/json' \
'http://ulapph-cloud-sca.appspot.com/process'
{
"total": 94.69999999999999,
"rules": {
"ult_small": "Rule_BuyThreePayTwoOnly"
},
"current": [
{
"code": "ult_large",
"name": "Unlimited 5GB",
"price": 44.9,
"items": 1,
"status": false
},
{
"code": "ult_small",
"name": "Unlimited 1GB",
"price": 24.9,
"items": 3,
"status": false
}
]
}
Scenario: The Unlimited 5GB Sim will have a bulk discount applied; whereby the price will drop to $39.90 each for the first month, if the customer buys more than 3.
cd test-cases/cart-test-scenario2
go test -v
//after this command, all tables are refreshed with data
[CLR0001] Test clear the database tables in appengine --> Products
[CLR0002] Test clear the database tables in appengine --> Cart
[ADD0001] Test add product 1 to catalog --> ult_small Unlimited 1GB $24.90
[ADD0002] Test add product 2 to catalog --> ult_medium Unlimited 2GB $29.90
[ADD0003] Test add product 3 to catalog --> ult_large Unlimited 5GB $44.90
[ADD0004] Test add product 4 to catalog --> 1gb 1 GB Data-pack $9.90
[SC20001] Test Add 2 items of Unlimited 1 GB for $24.90
[SC20001] Test Add 4 items of Unlimited 5 GB for $209.40
PASS
ok github.com/edwindvinas/shopping-cart-api/cart-test-scenario2 3.068s
//execute curl to simulate checkout of cart
$ curl -X GET --header 'Accept: application/json' \
'http://ulapph-cloud-sca.appspot.com/process'
{
"total": 209.39999999999998,
"rules": {
"ult_large": "Rule_BulkDiscountMoreThanThree"
},
"current": [
{
"code": "ult_large",
"name": "Unlimited 5GB",
"price": 44.9,
"items": 4,
"status": false
},
{
"code": "ult_small",
"name": "Unlimited 1GB",
"price": 24.9,
"items": 2,
"status": false
}
]
}
cd test-cases/cart-test-scenario2
go test -v
//after this command, all tables are refreshed with data
[CLR0001] Test clear the database tables in appengine --> Products
[CLR0002] Test clear the database tables in appengine --> Cart
[ADD0001] Test add product 1 to catalog --> ult_small Unlimited 1GB $24.90
[ADD0002] Test add product 2 to catalog --> ult_medium Unlimited 2GB $29.90
[ADD0003] Test add product 3 to catalog --> ult_large Unlimited 5GB $44.90
[ADD0004] Test add product 4 to catalog --> 1gb 1 GB Data-pack $9.90
[SC30001] Test Add 1 item of Unlimited 1 GB for $24.90
[SC30001] Test Add 2 items of Unlimited 2GB for $29.90
PASS
ok github.com/edwindvinas/shopping-cart-api/cart-test-scenario3 2.837s
//execute curl to simulate checkout of cart
$ curl -X GET --header 'Accept: application/json' \
'http://ulapph-cloud-sca.appspot.com/process'
{
"total": 84.69999999999999,
"rules": {},
"current": [
{
"code": "ult_large",
"name": "Unlimited 2GB",
"price": 29.9,
"items": 2,
"status": false
},
{
"code": "ult_small",
"name": "Unlimited 1GB",
"price": 24.9,
"items": 1,
"status": false
}
]
}
cd test-cases/cart-test-scenario2
go test -v
//after this command, all tables are refreshed with data
[CLR0001] Test clear the database tables in appengine --> Products
[CLR0002] Test clear the database tables in appengine --> Cart
[ADD0001] Test add product 1 to catalog --> ult_small Unlimited 1GB $24.90
[ADD0002] Test add product 2 to catalog --> ult_medium Unlimited 2GB $29.90
[ADD0003] Test add product 3 to catalog --> ult_large Unlimited 5GB $44.90
[ADD0004] Test add product 4 to catalog --> 1gb 1 GB Data-pack $9.90
[SC40001] Test Add 1 item of Unlimited 1 GB for $24.90
[SC40001] Test Add 1 item of 1 GB Data-pack $9.90
PASS
ok github.com/edwindvinas/shopping-cart-api/cart-test-scenario4 2.955s
//execute curl to simulate checkout of cart
$ curl -X GET --header 'Accept: application/json' 'http://ulapph-cloud-sca.appspot.com/process?promo_code=I%3C3AMAYSIM'
{
"total": 31.319999999999997,
"rules": {
"I\u003c3AMAYSIM": "Rule_PromoCodeDiscount"
},
"current": [
{
"code": "1gb",
"name": "1 GB Data-pack",
"price": 9.9,
"items": 1,
"status": false
},
{
"code": "ult_small",
"name": "Unlimited 1GB",
"price": 24.9,
"items": 1,
"status": false
}
]
}
- This is the swagger file which can be used to automatically generate the API server (in the future)
- See https://github.com/go-swagger/go-swagger for more info
- In reality, we shouldn't be coding the API server by hand coz go-swagger takes the swagger.json as input and it generates the API endpoints skeleton
- All we need to do is code the business that will handle each API call
- This is to avoid recompiling the code everytime we need to update the rules
Of course, more coding of the missing endpoints such as rules management and also very important to incorporate is the payment services such as Paypal etc
- Github for being awesome!
- Notepad+++ as very reliable and fast editor!
- Git Bash for Windows
- https://stackedit.io/editor for creating this markdown for Github!
[2017-Jan-22] Initial version released