From 321004a325415c9e4f47d3be4170d924542fd034 Mon Sep 17 00:00:00 2001 From: Jareth Gomes Date: Sun, 26 Sep 2021 14:49:55 -0500 Subject: [PATCH 1/5] Added service to manage prize shop --- config/dev_config.json | 4 + gateway/config/config.go | 7 ++ gateway/services/prize.go | 56 +++++++++++++ gateway/services/services.go | 2 + main.go | 2 + scripts/run.sh | 1 + services/prize/config/config.go | 40 +++++++++ services/prize/controller/controller.go | 86 +++++++++++++++++++ services/prize/main.go | 41 +++++++++ services/prize/models/prize.go | 9 ++ services/prize/models/prize_requests.go | 9 ++ services/prize/service/prize_service.go | 105 ++++++++++++++++++++++++ 12 files changed, 362 insertions(+) create mode 100644 gateway/services/prize.go create mode 100644 services/prize/config/config.go create mode 100644 services/prize/controller/controller.go create mode 100644 services/prize/main.go create mode 100644 services/prize/models/prize.go create mode 100644 services/prize/models/prize_requests.go create mode 100644 services/prize/service/prize_service.go 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/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..ad1f8cd2 --- /dev/null +++ b/gateway/services/prize.go @@ -0,0 +1,56 @@ +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, + }, +} + +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) +} 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..81cb0064 --- /dev/null +++ b/services/prize/controller/controller.go @@ -0,0 +1,86 @@ +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") +} + +/* + GetProfile is the endpoint to get the profile for the current user +*/ +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) + + 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) +} 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..0fd7d210 --- /dev/null +++ b/services/prize/models/prize_requests.go @@ -0,0 +1,9 @@ +package models + +type GetPrizeRequest struct { + ID string `json:"id"` +} + +type DeletePrizeRequest struct { + ID string `json:"id"` +} diff --git a/services/prize/service/prize_service.go b/services/prize/service/prize_service.go new file mode 100644 index 00000000..34f32840 --- /dev/null +++ b/services/prize/service/prize_service.go @@ -0,0 +1,105 @@ +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("prize", 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("prize", &prize) + + return err +} + +/* + Creates a prize +*/ +func UpdatePrize(prize models.Prize) error { + err := validate.Struct(prize) + + if err != nil { + return err + } + + selector := database.QuerySelector{ + "id": prize.ID, + } + + err = db.Update("prize", selector, &prize) + + return err +} + +func DeletePrize(prize_id string) error { + + selector := database.QuerySelector{ + "id": prize_id, + } + + err := db.RemoveOne("prize", selector) + + return err +} From 48ff687138cd107172102e49b697793c2dee0075 Mon Sep 17 00:00:00 2001 From: Jareth Gomes Date: Sun, 26 Sep 2021 22:46:38 -0500 Subject: [PATCH 2/5] Added internal routes to add and redeem shop points --- gateway/services/profile.go | 20 +++++ services/profile/config/config.go | 15 ++++ services/profile/controller/controller.go | 89 +++++++++++++++++++++ services/profile/models/checkin.go | 10 +++ services/profile/models/prize.go | 9 +++ services/profile/models/profile.go | 1 + services/profile/service/profile_service.go | 53 ++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 services/profile/models/prize.go diff --git a/gateway/services/profile.go b/gateway/services/profile.go index bcecd54a..aba08498 100644 --- a/gateway/services/profile.go +++ b/gateway/services/profile.go @@ -91,6 +91,18 @@ var ProfileRoutes = arbor.RouteCollection{ "/profile/favorite/remove/", alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.AttendeeRole, models.ApplicantRole, models.StaffRole, models.MentorRole}), middleware.IdentificationMiddleware).ThenFunc(RemoveProfileFavorite).ServeHTTP, }, + arbor.Route{ + "AwardShopPoints", + "POST", + "/profile/shoppoints/award/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(AwardShopPoints).ServeHTTP, + }, + arbor.Route{ + "AwardShopPoints", + "POST", + "/profile/shoppoints/redeem/", + alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(RedeemShopPoints).ServeHTTP, + }, // This needs to be the last route in order to prevent endpoints like "search", "leaderboard" from accidentally being routed as the {id} variable. arbor.Route{ "GetUserProfileById", @@ -155,3 +167,11 @@ func AddProfileFavorite(w http.ResponseWriter, r *http.Request) { func RemoveProfileFavorite(w http.ResponseWriter, r *http.Request) { arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) } + +func AwardShopPoints(w http.ResponseWriter, r *http.Request) { + arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) +} + +func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { + arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) +} diff --git a/services/profile/config/config.go b/services/profile/config/config.go index 7d072902..fe993b44 100644 --- a/services/profile/config/config.go +++ b/services/profile/config/config.go @@ -11,6 +11,9 @@ var PROFILE_DB_NAME string var PROFILE_PORT string +var PRIZE_DB_HOST string +var PRIZE_DB_NAME string + func Initialize() error { cfg_loader, err := configloader.Load(os.Getenv("HI_CONFIG")) @@ -36,5 +39,17 @@ func Initialize() error { 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 + } + return nil } diff --git a/services/profile/controller/controller.go b/services/profile/controller/controller.go index 3a577fad..3ea2aa74 100644 --- a/services/profile/controller/controller.go +++ b/services/profile/controller/controller.go @@ -32,6 +32,9 @@ func SetupController(route *mux.Route) { router.HandleFunc("/favorite/remove/", RemoveProfileFavorite).Methods("POST") router.HandleFunc("/{id}/", GetProfileById).Methods("GET") + + router.HandleFunc("/shoppoints/award/", AwardShopPoints).Methods("POST") + router.HandleFunc("/shoppoints/redeem/", RedeemShopPoints).Methods("POST") } /* @@ -145,6 +148,10 @@ func UpdateProfile(w http.ResponseWriter, r *http.Request) { profile.Points = old_profile.Points } + if profile.ShopPoints != old_profile.ShopPoints { + profile.ShopPoints = old_profile.ShopPoints + } + err = service.UpdateProfile(profile_id, profile) if err != nil { @@ -422,3 +429,85 @@ func RemoveProfileFavorite(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(favorites) } + +/* + AwardPoints gives the specified number of points to the current user. +*/ +func AwardShopPoints(w http.ResponseWriter, r *http.Request) { + var request models.AwardShopPointsRequest + json.NewDecoder(r.Body).Decode(&request) + + id := request.ID + + profile_id, err := service.GetProfileIdFromUserId(id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile id associated with the user")) + return + } + + user_profile, err := service.GetProfile(profile_id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile for id "+request.ID+" when trying to award points.")) + return + } + + user_profile.ShopPoints += request.ShopPoints + + err = service.UpdateProfile(profile_id, *user_profile) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update the profile when trying to award points.")) + return + } + + updated_profile, err := service.GetProfile(profile_id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get updated profile details after awarding points.")) + return + } + + json.NewEncoder(w).Encode(updated_profile) +} + +/* + AwardPoints gives the specified number of points to the current user. +*/ +func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { + var request models.RedeemShopPointsRequest + json.NewDecoder(r.Body).Decode(&request) + + id := request.ID + + profile_id, err := service.GetProfileIdFromUserId(id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile id associated with the user")) + return + } + + user_profile, err := service.GetProfile(profile_id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile for id "+request.ID+" when trying to award points.")) + return + } + + err = service.RedeemShopItem(request.ShopItemID, *user_profile) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update the profile when trying to award points.")) + return + } + + updated_profile, err := service.GetProfile(profile_id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get updated profile details after awarding points.")) + return + } + + json.NewEncoder(w).Encode(updated_profile) +} 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"` +} diff --git a/services/profile/models/profile.go b/services/profile/models/profile.go index 9db48172..4ef90bbe 100644 --- a/services/profile/models/profile.go +++ b/services/profile/models/profile.go @@ -5,6 +5,7 @@ type Profile struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` Points int `json:"points"` + ShopPoints int `json:"shopPoints"` Timezone string `json:"timezone"` Description string `json:"description"` Discord string `json:"discord"` diff --git a/services/profile/service/profile_service.go b/services/profile/service/profile_service.go index b4e9ab90..bd03c664 100644 --- a/services/profile/service/profile_service.go +++ b/services/profile/service/profile_service.go @@ -14,6 +14,7 @@ import ( var validate *validator.Validate var db database.Database +var prize_db database.Database func Initialize() error { if db != nil { @@ -28,6 +29,12 @@ func Initialize() error { return err } + prize_db, err = database.InitDatabase(config.PRIZE_DB_HOST, config.PRIZE_DB_NAME) + + if err != nil { + return err + } + validate = validator.New() return nil @@ -444,3 +451,49 @@ func RemoveProfileFavorite(profile_id string, profile string) error { return err } + +func RedeemShopItem(shop_item_id string, profile models.Profile) error { + query := database.QuerySelector{ + "id": shop_item_id, + } + + var prize models.Prize + err := prize_db.FindOne("prize", query, &prize) + + if err != nil { + return errors.New("Item trying to redeem doesn't exist") + } + + if prize.Quantity <= 0 { + return errors.New("Item is out of stock") + } + + if prize.Value > profile.ShopPoints { + return errors.New("User has insufficient points") + } + + profile.ShopPoints -= prize.Value // Note: prize.Value can be negative + prize.Quantity -= 1 + + // TODO: Associate redeemed items with profile + + err = prize_db.Update("prize", query, &prize) + + if err != nil { + return errors.New("Prize item could not be updated") + } + + selector := database.QuerySelector{ + "id": profile.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("profiles", selector, profile) + + if err != nil { + return errors.New("Profile could not be updated") + } + + return nil +} From 7488469d665b7eeacd70b98e0f4fcc5c85a41029 Mon Sep 17 00:00:00 2001 From: Jareth Gomes Date: Mon, 27 Sep 2021 02:45:25 -0500 Subject: [PATCH 3/5] Added tests to prize service --- services/prize/tests/prize_test.go | 296 +++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 services/prize/tests/prize_test.go 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) +} From 57655159bbfec320c05623dfaba7ebc345177a84 Mon Sep 17 00:00:00 2001 From: Jareth Gomes Date: Mon, 27 Sep 2021 02:49:47 -0500 Subject: [PATCH 4/5] Added prize service configs for tests --- Makefile | 2 +- config/test_config.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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/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", From 7e32ecdcf8d56ddaed4d39ecbed53b486ac9f5e8 Mon Sep 17 00:00:00 2001 From: Jareth Gomes Date: Mon, 27 Sep 2021 02:51:26 -0500 Subject: [PATCH 5/5] Moved awarding and redeeming points to prize service --- gateway/services/prize.go | 20 +++ gateway/services/profile.go | 20 --- services/prize/controller/controller.go | 52 +++++++- services/prize/models/prize_requests.go | 10 ++ services/prize/models/prize_responses.go | 5 + services/prize/models/user_points.go | 6 + services/prize/service/prize_service.go | 138 +++++++++++++++++++- services/profile/config/config.go | 15 --- services/profile/controller/controller.go | 89 ------------- services/profile/models/profile.go | 1 - services/profile/service/profile_service.go | 53 -------- 11 files changed, 223 insertions(+), 186 deletions(-) create mode 100644 services/prize/models/prize_responses.go create mode 100644 services/prize/models/user_points.go diff --git a/gateway/services/prize.go b/gateway/services/prize.go index ad1f8cd2..fb852f01 100644 --- a/gateway/services/prize.go +++ b/gateway/services/prize.go @@ -37,6 +37,18 @@ var PrizeRoutes = arbor.RouteCollection{ "/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) { @@ -54,3 +66,11 @@ func UpdatePrize(w http.ResponseWriter, r *http.Request) { 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/profile.go b/gateway/services/profile.go index aba08498..bcecd54a 100644 --- a/gateway/services/profile.go +++ b/gateway/services/profile.go @@ -91,18 +91,6 @@ var ProfileRoutes = arbor.RouteCollection{ "/profile/favorite/remove/", alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.AttendeeRole, models.ApplicantRole, models.StaffRole, models.MentorRole}), middleware.IdentificationMiddleware).ThenFunc(RemoveProfileFavorite).ServeHTTP, }, - arbor.Route{ - "AwardShopPoints", - "POST", - "/profile/shoppoints/award/", - alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(AwardShopPoints).ServeHTTP, - }, - arbor.Route{ - "AwardShopPoints", - "POST", - "/profile/shoppoints/redeem/", - alice.New(middleware.AuthMiddleware([]models.Role{models.AdminRole, models.StaffRole}), middleware.IdentificationMiddleware).ThenFunc(RedeemShopPoints).ServeHTTP, - }, // This needs to be the last route in order to prevent endpoints like "search", "leaderboard" from accidentally being routed as the {id} variable. arbor.Route{ "GetUserProfileById", @@ -167,11 +155,3 @@ func AddProfileFavorite(w http.ResponseWriter, r *http.Request) { func RemoveProfileFavorite(w http.ResponseWriter, r *http.Request) { arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) } - -func AwardShopPoints(w http.ResponseWriter, r *http.Request) { - arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) -} - -func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { - arbor.POST(w, config.PROFILE_SERVICE+r.URL.String(), ProfileFormat, "", r) -} diff --git a/services/prize/controller/controller.go b/services/prize/controller/controller.go index 81cb0064..3dafc694 100644 --- a/services/prize/controller/controller.go +++ b/services/prize/controller/controller.go @@ -17,10 +17,13 @@ func SetupController(route *mux.Route) { 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") } /* - GetProfile is the endpoint to get the profile for the current user + 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 @@ -54,7 +57,7 @@ func UpdatePrize(w http.ResponseWriter, r *http.Request) { var prize models.Prize json.NewDecoder(r.Body).Decode(&prize) - err := service.UpdatePrize(prize) + err := service.UpdatePrize(prize.ID, prize) if err != nil { errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update a new prize")) @@ -84,3 +87,48 @@ func DeletePrize(w http.ResponseWriter, r *http.Request) { 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/models/prize_requests.go b/services/prize/models/prize_requests.go index 0fd7d210..047afaa7 100644 --- a/services/prize/models/prize_requests.go +++ b/services/prize/models/prize_requests.go @@ -7,3 +7,13 @@ type GetPrizeRequest struct { 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 index 34f32840..e209b6c3 100644 --- a/services/prize/service/prize_service.go +++ b/services/prize/service/prize_service.go @@ -47,7 +47,7 @@ func GetPrize(prize_id string) (*models.Prize, error) { } var prize models.Prize - err := db.FindOne("prize", query, &prize) + err := db.FindOne("prizes", query, &prize) if err != nil { return nil, err @@ -69,7 +69,7 @@ func CreatePrize(prize models.Prize) error { return errors.New("Prize already exists") } - err = db.Insert("prize", &prize) + err = db.Insert("prizes", &prize) return err } @@ -77,7 +77,7 @@ func CreatePrize(prize models.Prize) error { /* Creates a prize */ -func UpdatePrize(prize models.Prize) error { +func UpdatePrize(prize_id string, prize models.Prize) error { err := validate.Struct(prize) if err != nil { @@ -85,21 +85,147 @@ func UpdatePrize(prize models.Prize) error { } selector := database.QuerySelector{ - "id": prize.ID, + "id": prize_id, } - err = db.Update("prize", selector, &prize) + 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("prize", selector) + 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/profile/config/config.go b/services/profile/config/config.go index fe993b44..7d072902 100644 --- a/services/profile/config/config.go +++ b/services/profile/config/config.go @@ -11,9 +11,6 @@ var PROFILE_DB_NAME string var PROFILE_PORT string -var PRIZE_DB_HOST string -var PRIZE_DB_NAME string - func Initialize() error { cfg_loader, err := configloader.Load(os.Getenv("HI_CONFIG")) @@ -39,17 +36,5 @@ func Initialize() error { 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 - } - return nil } diff --git a/services/profile/controller/controller.go b/services/profile/controller/controller.go index 3ea2aa74..3a577fad 100644 --- a/services/profile/controller/controller.go +++ b/services/profile/controller/controller.go @@ -32,9 +32,6 @@ func SetupController(route *mux.Route) { router.HandleFunc("/favorite/remove/", RemoveProfileFavorite).Methods("POST") router.HandleFunc("/{id}/", GetProfileById).Methods("GET") - - router.HandleFunc("/shoppoints/award/", AwardShopPoints).Methods("POST") - router.HandleFunc("/shoppoints/redeem/", RedeemShopPoints).Methods("POST") } /* @@ -148,10 +145,6 @@ func UpdateProfile(w http.ResponseWriter, r *http.Request) { profile.Points = old_profile.Points } - if profile.ShopPoints != old_profile.ShopPoints { - profile.ShopPoints = old_profile.ShopPoints - } - err = service.UpdateProfile(profile_id, profile) if err != nil { @@ -429,85 +422,3 @@ func RemoveProfileFavorite(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(favorites) } - -/* - AwardPoints gives the specified number of points to the current user. -*/ -func AwardShopPoints(w http.ResponseWriter, r *http.Request) { - var request models.AwardShopPointsRequest - json.NewDecoder(r.Body).Decode(&request) - - id := request.ID - - profile_id, err := service.GetProfileIdFromUserId(id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile id associated with the user")) - return - } - - user_profile, err := service.GetProfile(profile_id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile for id "+request.ID+" when trying to award points.")) - return - } - - user_profile.ShopPoints += request.ShopPoints - - err = service.UpdateProfile(profile_id, *user_profile) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update the profile when trying to award points.")) - return - } - - updated_profile, err := service.GetProfile(profile_id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get updated profile details after awarding points.")) - return - } - - json.NewEncoder(w).Encode(updated_profile) -} - -/* - AwardPoints gives the specified number of points to the current user. -*/ -func RedeemShopPoints(w http.ResponseWriter, r *http.Request) { - var request models.RedeemShopPointsRequest - json.NewDecoder(r.Body).Decode(&request) - - id := request.ID - - profile_id, err := service.GetProfileIdFromUserId(id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile id associated with the user")) - return - } - - user_profile, err := service.GetProfile(profile_id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get profile for id "+request.ID+" when trying to award points.")) - return - } - - err = service.RedeemShopItem(request.ShopItemID, *user_profile) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not update the profile when trying to award points.")) - return - } - - updated_profile, err := service.GetProfile(profile_id) - - if err != nil { - errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not get updated profile details after awarding points.")) - return - } - - json.NewEncoder(w).Encode(updated_profile) -} diff --git a/services/profile/models/profile.go b/services/profile/models/profile.go index 4ef90bbe..9db48172 100644 --- a/services/profile/models/profile.go +++ b/services/profile/models/profile.go @@ -5,7 +5,6 @@ type Profile struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` Points int `json:"points"` - ShopPoints int `json:"shopPoints"` Timezone string `json:"timezone"` Description string `json:"description"` Discord string `json:"discord"` diff --git a/services/profile/service/profile_service.go b/services/profile/service/profile_service.go index bd03c664..b4e9ab90 100644 --- a/services/profile/service/profile_service.go +++ b/services/profile/service/profile_service.go @@ -14,7 +14,6 @@ import ( var validate *validator.Validate var db database.Database -var prize_db database.Database func Initialize() error { if db != nil { @@ -29,12 +28,6 @@ func Initialize() error { return err } - prize_db, err = database.InitDatabase(config.PRIZE_DB_HOST, config.PRIZE_DB_NAME) - - if err != nil { - return err - } - validate = validator.New() return nil @@ -451,49 +444,3 @@ func RemoveProfileFavorite(profile_id string, profile string) error { return err } - -func RedeemShopItem(shop_item_id string, profile models.Profile) error { - query := database.QuerySelector{ - "id": shop_item_id, - } - - var prize models.Prize - err := prize_db.FindOne("prize", query, &prize) - - if err != nil { - return errors.New("Item trying to redeem doesn't exist") - } - - if prize.Quantity <= 0 { - return errors.New("Item is out of stock") - } - - if prize.Value > profile.ShopPoints { - return errors.New("User has insufficient points") - } - - profile.ShopPoints -= prize.Value // Note: prize.Value can be negative - prize.Quantity -= 1 - - // TODO: Associate redeemed items with profile - - err = prize_db.Update("prize", query, &prize) - - if err != nil { - return errors.New("Prize item could not be updated") - } - - selector := database.QuerySelector{ - "id": profile.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("profiles", selector, profile) - - if err != nil { - return errors.New("Profile could not be updated") - } - - return nil -}