diff --git a/common/datastore/conversions.go b/common/datastore/conversions.go index b4618f31..996d98f6 100644 --- a/common/datastore/conversions.go +++ b/common/datastore/conversions.go @@ -60,7 +60,9 @@ func toObject(raw_data interface{}, definition DataStoreDefinition) (interface{} return nil, err } } else { - data[field.Name] = getDefaultValue(field.Type) + if len(field.Fields) > 0 { + data[field.Name] = getDefaultValue(field.Type) + } } } diff --git a/documentation/docs/reference/services/Registration.md b/documentation/docs/reference/services/Registration.md index eefc5123..64616609 100644 --- a/documentation/docs/reference/services/Registration.md +++ b/documentation/docs/reference/services/Registration.md @@ -423,6 +423,71 @@ Response format: } ``` +PATCH /registration/attendee/ +------------------ + +Updates only the provided fields the registration for the user with the `id` in the JWT token provided in the Authorization header. + +Request format: +``` +{ + "email": "edited@gmail.com", + "lastName": "editedLastName", + "github": "editedGithub", + "graduationYear": 2023, + "interests": [ + "INTERNSHIP", + "JOB" + ], + "isOSContributor": true +} +``` + +Response format: +``` +{ + "age": 19, + "beginnerInfo": { + "pullRequest": 1, + "technicalSkills": [ + "Java" + ], + "versionControl": 3, + "yearsExperience": 3 + }, + "createdAt": 111111111, + "diet": [ + "VEGAN" + ], + "email": "edited@gmail.com", + "extraInfo": "", + "firstName": "John", + "gender": "MALE", + "github": "editedGithub", + "graduationYear": 2023, + "id": "github0000001", + "interests": [ + "INTERNSHIP", + "JOB" + ], + "isBeginner": false, + "isOSContributor": true, + "lastName": "editedLastName", + "linkedin": "", + "major": "Computer Science", + "phone": "0000000000", + "priorAttendance": true, + "school": "UIUC", + "shirtSize": "M", + "skills": [ + "C++" + ], + "teamMembers": null, + "transportation": "NONE", + "updatedAt": 1574291036 +} +``` + GET /registration/mentor/USERID/ ------------------------- diff --git a/gateway/services/registration.go b/gateway/services/registration.go index 757f8b61..f043e4c0 100644 --- a/gateway/services/registration.go +++ b/gateway/services/registration.go @@ -36,6 +36,12 @@ var RegistrationRoutes = arbor.RouteCollection{ "/registration/attendee/", alice.New(middleware.AuthMiddleware([]models.Role{models.ApplicantRole}), middleware.IdentificationMiddleware).ThenFunc(UpdateRegistration).ServeHTTP, }, + arbor.Route{ + "PatchCurrentUserRegistration", + "PATCH", + "/registration/attendee/", + alice.New(middleware.AuthMiddleware([]models.Role{models.UserRole}), middleware.IdentificationMiddleware).ThenFunc(PatchRegistration).ServeHTTP, + }, arbor.Route{ "GetFilteredUserRegistrations", "GET", @@ -97,3 +103,7 @@ func CreateRegistration(w http.ResponseWriter, r *http.Request) { func UpdateRegistration(w http.ResponseWriter, r *http.Request) { arbor.PUT(w, config.REGISTRATION_SERVICE+r.URL.String(), RegistrationFormat, "", r) } + +func PatchRegistration(w http.ResponseWriter, r *http.Request) { + arbor.PATCH(w, config.REGISTRATION_SERVICE+r.URL.String(), RegistrationFormat, "", r) +} diff --git a/services/registration/controller/controller.go b/services/registration/controller/controller.go index 1ba3bdfc..e3a489d7 100644 --- a/services/registration/controller/controller.go +++ b/services/registration/controller/controller.go @@ -20,9 +20,10 @@ func SetupController(route *mux.Route) { router.HandleFunc("/attendee/", GetCurrentUserRegistration).Methods("GET") router.HandleFunc("/attendee/", CreateCurrentUserRegistration).Methods("POST") router.HandleFunc("/attendee/", UpdateCurrentUserRegistration).Methods("PUT") + router.HandleFunc("/attendee/", PatchCurrentUserRegistration).Methods("PATCH") router.HandleFunc("/attendee/filter/", GetFilteredUserRegistrations).Methods("GET") - - router.HandleFunc("/mentor/", GetCurrentMentorRegistration).Methods("GET") + + router.HandleFunc("/mentor/", GetCurrentMentorRegistration).Methods("GET") router.HandleFunc("/mentor/", CreateCurrentMentorRegistration).Methods("POST") router.HandleFunc("/mentor/", UpdateCurrentMentorRegistration).Methods("PUT") router.HandleFunc("/mentor/filter/", GetFilteredMentorRegistrations).Methods("GET") @@ -260,6 +261,58 @@ func UpdateCurrentUserRegistration(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(updated_registration) } +/* + Endpoint to patch the registration for the current user. + On successful patch, sends the user a confirmation mail. +*/ +func PatchCurrentUserRegistration(w http.ResponseWriter, r *http.Request) { + id := r.Header.Get("HackIllinois-Identity") + + if id == "" { + errors.WriteError(w, r, errors.MalformedRequestError("Must provide id in request.", "Must provide id in request.")) + return + } + + user_registration := datastore.NewDataStore(config.REGISTRATION_DEFINITION) + + // Decode http request and write into user_registration + err := json.NewDecoder(r.Body).Decode(&user_registration) + + if err != nil { + errors.WriteError(w, r, errors.InternalError(err.Error(), "Could not decode user registration information. Possible failure in JSON validation, or invalid registration format.")) + return + } + + user_registration.Data["id"] = id + + user_registration.Data["updatedAt"] = time.Now().Unix() + + err = service.PatchUserRegistration(id, user_registration) + + if err != nil { + errors.WriteError(w, r, errors.InternalError(err.Error(), "Could not update user's registration.")) + return + } + + updated_registration, err := service.GetUserRegistration(id) + + if err != nil { + errors.WriteError(w, r, errors.DatabaseError(err.Error(), "Could not fetch user's updated registration.")) + return + } + + mail_template := "registration_update" + err = service.SendUserMail(id, mail_template) + + if err != nil { + errors.WriteError(w, r, errors.InternalError(err.Error(), "Could not send registration update email.")) + return + } + + json.NewEncoder(w).Encode(updated_registration) + return +} + /* Endpoint to get user registrations based on filters */ diff --git a/services/registration/service/registration_service.go b/services/registration/service/registration_service.go index 0509f13b..a3d2f170 100644 --- a/services/registration/service/registration_service.go +++ b/services/registration/service/registration_service.go @@ -90,7 +90,25 @@ func UpdateUserRegistration(id string, user_registration models.UserRegistration } /* - Returns db search query based on given parameters + Patches the registration associated with the given user id +*/ +func PatchUserRegistration(id string, user_registration models.UserRegistration) error { + selector := database.QuerySelector{"id": id} + + // Delete fields that weren't provided + for k, v := range user_registration.Data { + if v == nil { + delete(user_registration.Data, k) + } + } + + err := db.Patch("attendees", selector, &user_registration.Data) + + return err +} + +/* + Returns db search query based on given parameters */ func getFilterQuery(parameters map[string][]string) (map[string]interface{}, error) { query := make(map[string]interface{}) diff --git a/services/registration/tests/registration_test.go b/services/registration/tests/registration_test.go index 3af47484..02f6c96e 100644 --- a/services/registration/tests/registration_test.go +++ b/services/registration/tests/registration_test.go @@ -162,6 +162,50 @@ func TestUpdateUserRegistrationService(t *testing.T) { CleanupTestDB(t) } +/* + Service level test for updating user registration in the db +*/ +func TestPatchUserRegistrationService(t *testing.T) { + SetupTestDB(t) + updated_registration := getEmptyUserRegistration() + updated_registration.Data["email"] = "edited@gmail.com" + updated_registration.Data["isBeginner"] = true + updated_registration.Data["priorAttendance"] = false + updated_registration.Data["age"] = 22 + + err := service.PatchUserRegistration("testid", updated_registration) + + // if err != nil { + // t.Fatal(err) + // } + + user_registration, err := service.GetUserRegistration("testid") + + if err != nil { + t.Fatal(err) + } + + expected_registration := getBaseUserRegistration() + expected_registration.Data["id"] = "testid" + expected_registration.Data["firstName"] = "first" + expected_registration.Data["lastName"] = "last" + expected_registration.Data["shirtSize"] = "M" + expected_registration.Data["github"] = "githubusername" + expected_registration.Data["linkedin"] = "linkedinusername" + expected_registration.Data["createdAt"] = int64(10) + + expected_registration.Data["email"] = "edited@gmail.com" + expected_registration.Data["isBeginner"] = true + expected_registration.Data["priorAttendance"] = false + expected_registration.Data["age"] = 22 + + if !reflect.DeepEqual(user_registration.Data, expected_registration.Data) { + t.Errorf("Wrong user info.\nExpected %v\nGot %v\n", expected_registration.Data, user_registration.Data) + } + + CleanupTestDB(t) +} + /* Service level test for getting mentor registration from db */ @@ -426,6 +470,16 @@ func getBaseMentorRegistration() datastore.DataStore { return base_mentor_registration } +/* + Returns an empty user registration +*/ +func getEmptyUserRegistration() datastore.DataStore { + empty_user_registration := datastore.NewDataStore(config.REGISTRATION_DEFINITION) + json.Unmarshal([]byte(empty_registration_data), &empty_user_registration) + return empty_user_registration +} + +var empty_registration_data string = `{}` var user_registration_data string = ` { "id": "testid",