diff --git a/Makefile b/Makefile index d9aec5ac..603a49a9 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ REPO_ROOT := $(shell git rev-parse --show-toplevel) # SERVICES is the list services to test, services are located at $(REPO_ROOT)/service/ # GATEWAYS is the list of top level directories to test, gateways are located at $(REPO_ROOT)/ # Add new services or top level directories to test here -SERVICES := auth user registration decision rsvp checkin upload mail event stat notifications project profile +SERVICES := auth user registration decision rsvp checkin upload mail event stat notifications project profile prize GATEWAYS := gateway common # UTILITIES is the list of utilities to build, utilities are located at $(REPO_ROOT)/utilities/ diff --git a/config/dev_config.json b/config/dev_config.json index 88c4f8f9..d45444ba 100644 --- a/config/dev_config.json +++ b/config/dev_config.json @@ -12,6 +12,7 @@ "NOTIFICATIONS_SERVICE": "http://localhost:8012", "PROJECT_SERVICE": "http://localhost:8013", "PROFILE_SERVICE": "http://localhost:8014", + "PRIZE_SERVICE": "http://localhost:8015", "GATEWAY_PORT": "8000", "AUTH_PORT": ":8002", @@ -27,6 +28,7 @@ "NOTIFICATIONS_PORT": ":8012", "PROJECT_PORT": ":8013", "PROFILE_PORT": ":8014", + "PRIZE_PORT": ":8015", "AUTH_DB_HOST": "localhost", "USER_DB_HOST": "localhost", @@ -41,6 +43,7 @@ "NOTIFICATIONS_DB_HOST": "localhost", "PROJECT_DB_HOST": "localhost", "PROFILE_DB_HOST": "localhost", + "PRIZE_DB_HOST": "localhost", "AUTH_DB_NAME": "auth", "USER_DB_NAME": "user", @@ -55,6 +58,7 @@ "NOTIFICATIONS_DB_NAME": "notifications", "PROJECT_DB_NAME": "project", "PROFILE_DB_NAME": "profile", + "PRIZE_DB_NAME": "prize", "S3_REGION": "us-east-1", "S3_BUCKET": "hackillinois-upload-2019", diff --git a/config/test_config.json b/config/test_config.json index 53dee5a2..4ebcb0ca 100644 --- a/config/test_config.json +++ b/config/test_config.json @@ -12,6 +12,7 @@ "NOTIFICATIONS_SERVICE": "http://localhost:8012", "PROJECT_SERVICE": "http://localhost:8013", "PROFILE_SERVICE": "http://localhost:8014", + "PRIZE_SERVICE": "http://localhost:8015", "GATEWAY_PORT": "8000", "AUTH_PORT": ":8002", @@ -27,6 +28,7 @@ "NOTIFICATIONS_PORT": ":8012", "PROJECT_PORT": ":8013", "PROFILE_PORT": ":8014", + "PRIZE_PORT": ":8015", "AUTH_DB_HOST": "localhost", "USER_DB_HOST": "localhost", @@ -41,6 +43,7 @@ "NOTIFICATIONS_DB_HOST": "localhost", "PROJECT_DB_HOST": "localhost", "PROFILE_DB_HOST": "localhost", + "PRIZE_DB_HOST": "localhost", "AUTH_DB_NAME": "test-auth", "USER_DB_NAME": "test-user", @@ -55,6 +58,7 @@ "NOTIFICATIONS_DB_NAME": "test-notifications", "PROJECT_DB_NAME": "test-project", "PROFILE_DB_NAME": "test-profile", + "PRIZE_DB_NAME": "test-prize", "S3_REGION": "us-east-1", "S3_BUCKET": "hackillinois-upload-2019", diff --git a/gateway/config/config.go b/gateway/config/config.go index 56931404..332d40f7 100644 --- a/gateway/config/config.go +++ b/gateway/config/config.go @@ -26,6 +26,7 @@ var STAT_SERVICE string var NOTIFICATIONS_SERVICE string var PROJECT_SERVICE string var PROFILE_SERVICE string +var PRIZE_SERVICE string func Initialize() error { @@ -119,6 +120,12 @@ func Initialize() error { return err } + PRIZE_SERVICE, err = cfg_loader.Get("PRIZE_SERVICE") + + if err != nil { + return err + } + port_str, err := cfg_loader.Get("GATEWAY_PORT") if err != nil { diff --git a/gateway/services/prize.go b/gateway/services/prize.go new file mode 100644 index 00000000..fb852f01 --- /dev/null +++ b/gateway/services/prize.go @@ -0,0 +1,76 @@ +package services + +import ( + "net/http" + + "github.com/HackIllinois/api/gateway/config" + "github.com/HackIllinois/api/gateway/middleware" + "github.com/HackIllinois/api/gateway/models" + "github.com/arbor-dev/arbor" + "github.com/justinas/alice" +) + +const PrizeFormat string = "JSON" + +var PrizeRoutes = arbor.RouteCollection{ + arbor.Route{ + "GetPrize", + "GET", + "/prize/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(GetPrize).ServeHTTP, + }, + arbor.Route{ + "CreatePrize", + "POST", + "/prize/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(CreatePrize).ServeHTTP, + }, + arbor.Route{ + "UpdatePrize", + "PUT", + "/prize/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(UpdatePrize).ServeHTTP, + }, + arbor.Route{ + "DeletePrize", + "DELETE", + "/prize/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(DeletePrize).ServeHTTP, + }, + arbor.Route{ + "AwardPoints", + "POST", + "/prize/points/award/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(AwardShopPoints).ServeHTTP, + }, + arbor.Route{ + "RedeemPoints", + "POST", + "/prize/points/redeem/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(RedeemShopPoints).ServeHTTP, + }, +} + +func GetPrize(w http.ResponseWriter, r *http.Request) { + arbor.GET(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} + +func CreatePrize(w http.ResponseWriter, r *http.Request) { + arbor.POST(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} + +func UpdatePrize(w http.ResponseWriter, r *http.Request) { + arbor.PUT(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} + +func DeletePrize(w http.ResponseWriter, r *http.Request) { + arbor.DELETE(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} + +func AwardShopPoints(w http.ResponseWriter, r *http.Request) { + arbor.POST(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} + +func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { + arbor.POST(w, config.PRIZE_SERVICE+r.URL.String(), PrizeFormat, "", r) +} diff --git a/gateway/services/services.go b/gateway/services/services.go index a626151e..9af07610 100644 --- a/gateway/services/services.go +++ b/gateway/services/services.go @@ -26,6 +26,7 @@ func Initialize() error { "notifications": config.NOTIFICATIONS_SERVICE, "project": config.PROJECT_SERVICE, "profile": config.PROFILE_SERVICE, + "prize": config.PRIZE_SERVICE, } return nil @@ -70,6 +71,7 @@ func RegisterAPIs() arbor.RouteCollection { Routes = append(Routes, ProfileRoutes...) Routes = append(Routes, HealthRoutes...) Routes = append(Routes, ReloadRoutes...) + Routes = append(Routes, PrizeRoutes...) return Routes } diff --git a/main.go b/main.go index 1b77c95f..ae328d72 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "github.com/HackIllinois/api/services/event" "github.com/HackIllinois/api/services/mail" "github.com/HackIllinois/api/services/notifications" + "github.com/HackIllinois/api/services/prize" "github.com/HackIllinois/api/services/profile" "github.com/HackIllinois/api/services/project" "github.com/HackIllinois/api/services/registration" @@ -38,6 +39,7 @@ var SERVICE_ENTRYPOINTS = map[string](func()){ "notifications": notifications.Entry, "project": project.Entry, "profile": profile.Entry, + "prize": prize.Entry, } func StartAll() { diff --git a/scripts/run.sh b/scripts/run.sh index fa7876c3..e8ee6ffb 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -29,6 +29,7 @@ $REPO_ROOT/bin/hackillinois-api --service stat & $REPO_ROOT/bin/hackillinois-api --service notifications & $REPO_ROOT/bin/hackillinois-api --service project & $REPO_ROOT/bin/hackillinois-api --service profile & +$REPO_ROOT/bin/hackillinois-api --service prize & $REPO_ROOT/bin/hackillinois-api --service gateway & diff --git a/services/prize/config/config.go b/services/prize/config/config.go new file mode 100644 index 00000000..ddcfb562 --- /dev/null +++ b/services/prize/config/config.go @@ -0,0 +1,40 @@ +package config + +import ( + "os" + + "github.com/HackIllinois/api/common/configloader" +) + +var PRIZE_DB_HOST string +var PRIZE_DB_NAME string + +var PRIZE_PORT string + +func Initialize() error { + cfg_loader, err := configloader.Load(os.Getenv("HI_CONFIG")) + + if err != nil { + return err + } + + PRIZE_DB_HOST, err = cfg_loader.Get("PRIZE_DB_HOST") + + if err != nil { + return err + } + + PRIZE_DB_NAME, err = cfg_loader.Get("PRIZE_DB_NAME") + + if err != nil { + return err + } + + PRIZE_PORT, err = cfg_loader.Get("PRIZE_PORT") + + if err != nil { + return err + } + + return nil +} diff --git a/services/prize/controller/controller.go b/services/prize/controller/controller.go new file mode 100644 index 00000000..3dafc694 --- /dev/null +++ b/services/prize/controller/controller.go @@ -0,0 +1,134 @@ +package controller + +import ( + "encoding/json" + "net/http" + + "github.com/HackIllinois/api/common/errors" + "github.com/HackIllinois/api/services/prize/models" + "github.com/HackIllinois/api/services/prize/service" + "github.com/gorilla/mux" +) + +func SetupController(route *mux.Route) { + router := route.Subrouter() + + router.HandleFunc("/", GetPrize).Methods("GET") + router.HandleFunc("/", CreatePrize).Methods("POST") + router.HandleFunc("/", UpdatePrize).Methods("PUT") + router.HandleFunc("/", DeletePrize).Methods("DELETE") + + router.HandleFunc("/points/award/", AwardShopPoints).Methods("POST") + router.HandleFunc("/points/redeem/", RedeemShopPoints).Methods("POST") +} + +/* + GetPrize is the endpoint to get a prize from the given prize id. +*/ +func GetPrize(w http.ResponseWriter, r *http.Request) { + var request models.GetPrizeRequest + json.NewDecoder(r.Body).Decode(&request) + + prize, err := service.GetPrize(request.ID) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get prize id")) + return + } + + json.NewEncoder(w).Encode(prize) +} + +func CreatePrize(w http.ResponseWriter, r *http.Request) { + var prize models.Prize + json.NewDecoder(r.Body).Decode(&prize) + + err := service.CreatePrize(prize) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not create a new prize")) + return + } + + json.NewEncoder(w).Encode(prize) +} + +func UpdatePrize(w http.ResponseWriter, r *http.Request) { + var prize models.Prize + json.NewDecoder(r.Body).Decode(&prize) + + err := service.UpdatePrize(prize.ID, prize) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update a new prize")) + return + } + + json.NewEncoder(w).Encode(prize) +} + +func DeletePrize(w http.ResponseWriter, r *http.Request) { + var request models.DeletePrizeRequest + json.NewDecoder(r.Body).Decode(&request) + + prize, err := service.GetPrize(request.ID) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Prize does not exist")) + return + } + + err = service.DeletePrize(request.ID) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not delete prize")) + return + } + + json.NewEncoder(w).Encode(prize) +} + +/* + AwardPoints gives the specified number of points to the current user. +*/ +func AwardShopPoints(w http.ResponseWriter, r *http.Request) { + var request models.AwardPointsRequest + json.NewDecoder(r.Body).Decode(&request) + + id := request.ID + add_points := request.ShopPoints + + user_points, err := service.AwardPoints(add_points, id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update the user's points")) + return + } + + json.NewEncoder(w).Encode(user_points) +} + +/* + RedeemPoints attempts to give the specified item to the user for points. +*/ +func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { + var request models.RedeemPointsRequest + json.NewDecoder(r.Body).Decode(&request) + + id := request.ID + item_id := request.ShopItemID + + err := service.RedeemPrize(item_id, id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get prize of id \""+item_id+"\" for user \""+id+"\".")) + return + } + + // Could have return UserPoints struct instead + res := models.RedeemPointsResponse{ + Status: "success", + } + + json.NewEncoder(w).Encode(res) +} diff --git a/services/prize/main.go b/services/prize/main.go new file mode 100644 index 00000000..0defddc9 --- /dev/null +++ b/services/prize/main.go @@ -0,0 +1,41 @@ +package prize + +import ( + "log" + + "github.com/HackIllinois/api/common/apiserver" + "github.com/HackIllinois/api/services/prize/config" + "github.com/HackIllinois/api/services/prize/controller" + "github.com/HackIllinois/api/services/prize/service" + "github.com/gorilla/mux" +) + +func Initialize() error { + err := config.Initialize() + + if err != nil { + return err + + } + + err = service.Initialize() + + if err != nil { + return err + } + + return nil +} + +func Entry() { + err := Initialize() + + if err != nil { + log.Fatal(err) + } + + router := mux.NewRouter() + controller.SetupController(router.PathPrefix("/prize")) + + log.Fatal(apiserver.StartServer(config.PRIZE_PORT, router, "prize", Initialize)) +} diff --git a/services/prize/models/prize.go b/services/prize/models/prize.go new file mode 100644 index 00000000..80b8d935 --- /dev/null +++ b/services/prize/models/prize.go @@ -0,0 +1,9 @@ +package models + +type Prize struct { + ID string `json:"id"` + Name string `json:"name"` + Value int `json:"value"` + Quantity int `json:"quantity"` + ImageUrl string `json:"imgURL"` +} diff --git a/services/prize/models/prize_requests.go b/services/prize/models/prize_requests.go new file mode 100644 index 00000000..047afaa7 --- /dev/null +++ b/services/prize/models/prize_requests.go @@ -0,0 +1,19 @@ +package models + +type GetPrizeRequest struct { + ID string `json:"id"` +} + +type DeletePrizeRequest struct { + ID string `json:"id"` +} + +type AwardPointsRequest struct { + ID string `json:"id"` + ShopPoints int `json:"shopPoints"` +} + +type RedeemPointsRequest struct { + ID string `json:"id"` + ShopItemID string `json:"itemID"` +} diff --git a/services/prize/models/prize_responses.go b/services/prize/models/prize_responses.go new file mode 100644 index 00000000..b2422e21 --- /dev/null +++ b/services/prize/models/prize_responses.go @@ -0,0 +1,5 @@ +package models + +type RedeemPointsResponse struct { + Status string `json:"status"` +} diff --git a/services/prize/models/user_points.go b/services/prize/models/user_points.go new file mode 100644 index 00000000..d22c2d52 --- /dev/null +++ b/services/prize/models/user_points.go @@ -0,0 +1,6 @@ +package models + +type UserPoints struct { + ID string `json:"id"` + Points int `json:"shopPoints"` +} diff --git a/services/prize/service/prize_service.go b/services/prize/service/prize_service.go new file mode 100644 index 00000000..e209b6c3 --- /dev/null +++ b/services/prize/service/prize_service.go @@ -0,0 +1,231 @@ +package service + +import ( + "errors" + + "github.com/HackIllinois/api/common/database" + "github.com/HackIllinois/api/services/prize/config" + "github.com/HackIllinois/api/services/prize/models" + "gopkg.in/go-playground/validator.v9" +) + +var validate *validator.Validate + +var db database.Database + +func Initialize() error { + if db != nil { + db.Close() + db = nil + } + + var err error + db, err = database.InitDatabase(config.PRIZE_DB_HOST, config.PRIZE_DB_NAME) + + if err != nil { + return err + } + + validate = validator.New() + + return nil +} + +/* + TODO: Returns all prizes +*/ +func GetAllPrizes() { + +} + +/* + Returns the prize with the given id +*/ +func GetPrize(prize_id string) (*models.Prize, error) { + query := database.QuerySelector{ + "id": prize_id, + } + + var prize models.Prize + err := db.FindOne("prizes", query, &prize) + + if err != nil { + return nil, err + } + + return &prize, nil +} + +/* + Creates a prize +*/ +func CreatePrize(prize models.Prize) error { + _, err := GetPrize(prize.ID) + + if err != database.ErrNotFound { + if err != nil { + return err + } + return errors.New("Prize already exists") + } + + err = db.Insert("prizes", &prize) + + return err +} + +/* + Creates a prize +*/ +func UpdatePrize(prize_id string, prize models.Prize) error { + err := validate.Struct(prize) + + if err != nil { + return err + } + + selector := database.QuerySelector{ + "id": prize_id, + } + + err = db.Update("prizes", selector, &prize) + + return err +} + +func GetPoints(user_id string) (*models.UserPoints, error) { + selector := database.QuerySelector{ + "id": user_id, + } + + var points models.UserPoints + err := db.FindOne("userpoints", selector, &points) + + if err != nil { + return nil, errors.New("Could not find given user id") + } + + return &points, nil +} + +func DeletePrize(prize_id string) error { + + selector := database.QuerySelector{ + "id": prize_id, + } + + err := db.RemoveOne("prizes", selector) + + return err +} + +func CreateUserPoints(user_id string, user_points *models.UserPoints) error { + selector := database.QuerySelector{ + "id": user_id, + } + + var user_pts models.UserPoints + err := db.FindOne("userpoints", selector, &user_pts) + + if err != database.ErrNotFound { + if err != nil { + return err + } + return errors.New("User points already exists") + } + + user_points.ID = user_id + user_points.Points = 0 + + err = db.Insert("userpoints", &user_points) + if err != nil { + return errors.New("Could not create user points") + } + + return nil +} + +func AwardPoints(points int, user_id string) (*models.UserPoints, error) { + selector := database.QuerySelector{ + "id": user_id, + } + + var user_points models.UserPoints + err := db.FindOne("userpoints", selector, &user_points) + + if err != nil { + // Could not find given user id, assuming zero points and creating new entry + err = CreateUserPoints(user_id, &user_points) + + if err != nil { + return nil, errors.New("Could not find or create user points") + } + } + + user_points.Points += points + + err = db.Update("userpoints", selector, &user_points) + + if err != nil { + return nil, errors.New("Could not update user points") + } + + return &user_points, nil +} + +func RedeemPrize(prize_item_id string, user_id string) error { + prize_query := database.QuerySelector{ + "id": prize_item_id, + } + + var prize models.Prize + err := db.FindOne("prizes", prize_query, &prize) + + if err != nil { + return errors.New("Item trying to redeem doesn't exist") + } + + points_query := database.QuerySelector{ + "id": user_id, + } + + var user_points models.UserPoints + err = db.FindOne("userpoints", points_query, &user_points) + + if err != nil { + return errors.New("User does not have any existing points") + } + + if prize.Quantity <= 0 { + return errors.New("Item is out of stock") + } + + if prize.Value > user_points.Points { + return errors.New("User has insufficient points") + } + + user_points.Points -= prize.Value // Note: prize.Value can be negative + prize.Quantity -= 1 + + // TODO: Associate redeemed items with profile + + err = db.Update("prizes", prize_query, &prize) + + if err != nil { + return errors.New("Prize item could not be updated") + } + + selector := database.QuerySelector{ + "id": user_points.ID, + } + // A rare case, but if this update fails, the user is not charged for the + // item and the quantity of the prize item decrements since they are + // individual commits. + err = db.Update("userpoints", selector, user_points) + + if err != nil { + return errors.New("Profile could not be updated") + } + + return nil +} diff --git a/services/prize/tests/prize_test.go b/services/prize/tests/prize_test.go new file mode 100644 index 00000000..77129128 --- /dev/null +++ b/services/prize/tests/prize_test.go @@ -0,0 +1,296 @@ +package tests + +import ( + "fmt" + "os" + "reflect" + "testing" + "time" + + "github.com/HackIllinois/api/common/database" + "github.com/HackIllinois/api/services/prize/config" + "github.com/HackIllinois/api/services/prize/models" + "github.com/HackIllinois/api/services/prize/service" +) + +var db database.Database + +func TestMain(m *testing.M) { + err := config.Initialize() + + if err != nil { + fmt.Printf("ERROR: %v\n", err) + os.Exit(1) + + } + + err = service.Initialize() + + if err != nil { + fmt.Printf("ERROR: %v\n", err) + os.Exit(1) + } + + db, err = database.InitDatabase(config.PRIZE_DB_HOST, config.PRIZE_DB_NAME) + + if err != nil { + fmt.Printf("ERROR: %v\n", err) + os.Exit(1) + } + + return_code := m.Run() + + os.Exit(return_code) +} + +var TestTime = time.Now().Unix() + +/* + Initialize db with a test prize +*/ +func SetupTestDB(t *testing.T) { + user_id := "testid" + + prize := models.Prize{ + ID: user_id, + Name: "testprizename", + Value: 233, + Quantity: 1337, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + points := models.UserPoints{ + ID: "testuserid", + Points: 500, + } + + err := db.Insert("userpoints", &points) + + if err != nil { + t.Fatal(err) + } + + err = db.Insert("prizes", &prize) + + if err != nil { + t.Fatal(err) + } +} + +/* + Drop test db +*/ +func CleanupTestDB(t *testing.T) { + err := db.DropDatabase() + + if err != nil { + t.Fatal(err) + } +} + +/* + Service level test for getting prize from db +*/ +func TestGetPrizeService(t *testing.T) { + SetupTestDB(t) + + prize, err := service.GetPrize("testid") + + if err != nil { + t.Fatal(err) + } + + expected_prize := models.Prize{ + ID: "testid", + Name: "testprizename", + Value: 233, + Quantity: 1337, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + if !reflect.DeepEqual(prize, &expected_prize) { + t.Errorf("Wrong prize info. Expected %v, got %v", &expected_prize, prize) + } + + CleanupTestDB(t) +} + +/* + Service level test for creating a prize in the db +*/ +func TestCreatePrizeService(t *testing.T) { + SetupTestDB(t) + + new_prize := models.Prize{ + ID: "testid2", + Name: "testfirstname", + Value: 5000, + Quantity: 30, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + err := service.CreatePrize(new_prize) + + if err != nil { + t.Fatal(err) + } + + prize, err := service.GetPrize(new_prize.ID) + + if err != nil { + t.Fatal(err) + } + + expected_prize := models.Prize{ + ID: "testid2", + Name: "testfirstname", + Value: 5000, + Quantity: 30, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + if !reflect.DeepEqual(prize, &expected_prize) { + t.Errorf("Wrong prize info. Expected %v, got %v", expected_prize, prize) + } + + CleanupTestDB(t) +} + +/* + Service level test for deleting a prize in the db +*/ +func TestDeletePrizeService(t *testing.T) { + SetupTestDB(t) + + prize_id := "testid" + + // Try to delete the profile + + err := service.DeletePrize(prize_id) + + if err != nil { + t.Fatal(err) + } + + // Try to find the profile in the profiles db + prize, err := service.GetPrize(prize_id) + + if err == nil { + t.Errorf("Found prize %v in prizes database.", prize) + } + + CleanupTestDB(t) +} + +/* + Service level test for updating a profile in the db +*/ +func TestUpdatePrizeService(t *testing.T) { + SetupTestDB(t) + + prize := models.Prize{ + ID: "testid", + Name: "another prize!", + Value: 1234, + Quantity: 9999, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + err := service.UpdatePrize(prize.ID, prize) + + if err != nil { + t.Fatal(err) + } + + updated_prize, err := service.GetPrize(prize.ID) + + if err != nil { + t.Fatal(err) + } + + expected_prize := models.Prize{ + ID: "testid", + Name: "another prize!", + Value: 1234, + Quantity: 9999, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + if !reflect.DeepEqual(updated_prize, &expected_prize) { + t.Errorf("Wrong prize info. Expected %v, got %v", expected_prize, updated_prize) + } + + CleanupTestDB(t) +} + +/* + Service level test for redeeming a prize +*/ +func TestAwardPointsService(t *testing.T) { + SetupTestDB(t) + + updated_points, err := service.AwardPoints(100, "testuserid") + + if err != nil { + t.Fatal(err) + } + + expected_points := models.UserPoints{ + ID: "testuserid", + Points: 600, + } + + if !reflect.DeepEqual(updated_points, &expected_points) { + t.Errorf("Wrong prize info. Expected %v, got %v", expected_points, updated_points) + } + + CleanupTestDB(t) +} + +/* + Service level test for redeeming a prize +*/ +func TestRedeemPrizeService(t *testing.T) { + SetupTestDB(t) + + err := service.RedeemPrize("testid", "testuserid") + + if err != nil { + t.Fatal(err) + } + + updated_prize, err := service.GetPrize("testid") + + if err != nil { + t.Fatal(err) + } + + expected_prize := models.Prize{ + ID: "testid", + Name: "testprizename", + Value: 233, + Quantity: 1336, + ImageUrl: "https://imgs.smoothradio.com/images/191589?crop=16_9&width=660&relax=1&signature=Rz93ikqcAz7BcX6SKiEC94zJnqo=", + } + + if !reflect.DeepEqual(updated_prize, &expected_prize) { + t.Errorf("Wrong prize info. Expected %v, got %v", expected_prize, updated_prize) + } + + updated_points, err := service.GetPoints("testuserid") + + if err != nil { + t.Fatal(err) + } + + expected_points := models.UserPoints{ + ID: "testuserid", + Points: 267, + } + + if !reflect.DeepEqual(updated_points, &expected_points) { + t.Errorf("Wrong user points info. Expected %v, got %v", expected_points, updated_points) + } + + CleanupTestDB(t) +} diff --git a/services/profile/models/checkin.go b/services/profile/models/checkin.go index 4b6deacd..a610e34c 100644 --- a/services/profile/models/checkin.go +++ b/services/profile/models/checkin.go @@ -13,3 +13,13 @@ type AwardPointsRequest struct { ID string `json:"id"` Points int `json:"points"` } + +type AwardShopPointsRequest struct { + ID string `json:"id"` + ShopPoints int `json:"shopPoints"` +} + +type RedeemShopPointsRequest struct { + ID string `json:"id"` + ShopItemID string `json:"itemID"` +} diff --git a/services/profile/models/prize.go b/services/profile/models/prize.go new file mode 100644 index 00000000..80b8d935 --- /dev/null +++ b/services/profile/models/prize.go @@ -0,0 +1,9 @@ +package models + +type Prize struct { + ID string `json:"id"` + Name string `json:"name"` + Value int `json:"value"` + Quantity int `json:"quantity"` + ImageUrl string `json:"imgURL"` +}