From 5a65b3a653eb4a9ca62332fa65c8d352e6dc782f Mon Sep 17 00:00:00 2001 From: Michael Hunteman Date: Sun, 20 Oct 2024 18:52:16 -0700 Subject: Use UUID --- client/.env.development | 2 +- client/.env.test | 2 +- client/src/components/RsvpForm.tsx | 11 +------- client/src/models.ts | 2 +- server/admin/models.go | 1 + server/admin/store.go | 2 +- server/cmd/main.go | 2 +- server/guest/handler.go | 31 ++++++++++------------- server/guest/models.go | 2 +- server/guest/store.go | 38 ++++++++++++++-------------- server/schema.sql | 8 +++--- server/test/guest_test.go | 52 ++++++++++++++++++++------------------ 12 files changed, 71 insertions(+), 82 deletions(-) diff --git a/client/.env.development b/client/.env.development index be2cee9..eab1509 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1 +1 @@ -VITE_BASE_URL=http://192.168.1.18:8080/api/ +VITE_BASE_URL=http://192.168.1.11:8080/api/ diff --git a/client/.env.test b/client/.env.test index be2cee9..eab1509 100644 --- a/client/.env.test +++ b/client/.env.test @@ -1 +1 @@ -VITE_BASE_URL=http://192.168.1.18:8080/api/ +VITE_BASE_URL=http://192.168.1.11:8080/api/ diff --git a/client/src/components/RsvpForm.tsx b/client/src/components/RsvpForm.tsx index 2a3552f..53fefa4 100644 --- a/client/src/components/RsvpForm.tsx +++ b/client/src/components/RsvpForm.tsx @@ -34,16 +34,7 @@ function RsvpForm() { watch, formState: { errors }, } = useForm({ - defaultValues: { - id: guest.id, - firstName: guest.firstName, - lastName: guest.lastName, - attendance: guest.attendance, - email: guest.email, - message: guest.message, - partySize: guest.partySize, - partyList: guest.partyList, - }, + defaultValues: guest, }); const onSubmit = async (data: Guest) => { diff --git a/client/src/models.ts b/client/src/models.ts index 201a969..c0cff9e 100644 --- a/client/src/models.ts +++ b/client/src/models.ts @@ -1,5 +1,5 @@ export interface Guest { - id?: number; + id?: string; firstName: string; lastName: string; attendance?: string; diff --git a/server/admin/models.go b/server/admin/models.go index 1699381..275f617 100644 --- a/server/admin/models.go +++ b/server/admin/models.go @@ -6,6 +6,7 @@ import ( ) type Admin struct { + Id string `json:"id"` Username string `json:"username"` Password string `json:"password"` } diff --git a/server/admin/store.go b/server/admin/store.go index f36b342..65b905a 100644 --- a/server/admin/store.go +++ b/server/admin/store.go @@ -36,7 +36,7 @@ func (store Store) Find(requestAdmin Admin) (Admin, error) { func createAdmin(requestAdmin Admin, adminRows pgx.Rows) (Admin, bool) { var databaseAdmin Admin for adminRows.Next() { - err := adminRows.Scan(&databaseAdmin.Username, &databaseAdmin.Password) + err := adminRows.Scan(&databaseAdmin.Id, &databaseAdmin.Username, &databaseAdmin.Password) if err != nil { return Admin{}, false } diff --git a/server/cmd/main.go b/server/cmd/main.go index b57ee30..92ca984 100644 --- a/server/cmd/main.go +++ b/server/cmd/main.go @@ -60,7 +60,7 @@ func writeMethods(responseWriter http.ResponseWriter, request *http.Request) { } func writeOrigins(responseWriter http.ResponseWriter, request *http.Request) { - allowedOrigins := []string{"http://localhost:5173", "http://192.168.1.18:5173"} + allowedOrigins := []string{"http://localhost:5173", "http://192.168.1.11:5173"} origin := request.Header.Get("Origin") if slices.Contains(allowedOrigins, origin) { responseWriter.Header().Add("Access-Control-Allow-Origin", origin) diff --git a/server/guest/handler.go b/server/guest/handler.go index 20ed2dc..62de5be 100644 --- a/server/guest/handler.go +++ b/server/guest/handler.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "regexp" - "strconv" "time" "github.com/golang-jwt/jwt/v5" @@ -14,7 +13,7 @@ import ( var ( guestRegex = regexp.MustCompile(`^/api/guests/*$`) - guestIDRegex = regexp.MustCompile(`^/api/guests/([0-9]+)$`) + guestIdRegex = regexp.MustCompile(`^/api/guests/([a-z0-9-]+)$`) ) type GuestHandler struct { @@ -26,7 +25,7 @@ type GuestStore interface { Get() ([]Guest, error) Add(guest Guest) error Update(guest Guest) error - Delete(id int) error + Delete(id string) error } type appError struct { @@ -48,13 +47,13 @@ func (handler *GuestHandler) ServeHTTP(responseWriter http.ResponseWriter, responseWriter.WriteHeader(http.StatusOK) case request.Method == http.MethodPost && request.URL.Path == "/api/guests/login": handler.handleLogIn(responseWriter, request) - case request.Method == http.MethodPut && guestIDRegex.MatchString(request.URL.Path): + case request.Method == http.MethodPut && guestIdRegex.MatchString(request.URL.Path): handler.handlePut(responseWriter, request) case request.Method == http.MethodGet && guestRegex.MatchString(request.URL.Path): handler.handleGet(responseWriter, request) case request.Method == http.MethodPost && guestRegex.MatchString(request.URL.Path): handler.handlePost(responseWriter, request) - case request.Method == http.MethodDelete && guestIDRegex.MatchString(request.URL.Path): + case request.Method == http.MethodDelete && guestIdRegex.MatchString(request.URL.Path): handler.handleDelete(responseWriter, request) default: responseWriter.WriteHeader(http.StatusNotFound) @@ -194,7 +193,7 @@ func (handler *GuestHandler) putGuest(request *http.Request) *appError { if err := handler.validateToken(request, guestKey); err != nil { return err } - if handler.findID(request) { + if handler.findId(request) { return &appError{errors.New("ID not found"), "{ \"message\": \"ID not found\" }", http.StatusNotFound} } @@ -244,8 +243,8 @@ func (handler *GuestHandler) parseWithClaims(token string, claims *Claims, }) } -func (handler *GuestHandler) findID(request *http.Request) bool { - matches := guestIDRegex.FindStringSubmatch(request.URL.Path) +func (handler *GuestHandler) findId(request *http.Request) bool { + matches := guestIdRegex.FindStringSubmatch(request.URL.Path) return len(matches) < 2 } @@ -310,7 +309,7 @@ func (handler *GuestHandler) postGuest(request *http.Request) *appError { func (handler *GuestHandler) checkExistingGuests(guests []Guest, newGuest Guest) error { for _, guest := range guests { - if guest.ID == newGuest.ID { + if guest.Id == newGuest.Id { return errors.New("ID already exists") } } @@ -326,15 +325,11 @@ func (handler *GuestHandler) deleteGuest(request *http.Request) *appError { if err := handler.validateToken(request, adminKey); err != nil { return err } - if handler.findID(request) { + if handler.findId(request) { return &appError{errors.New("ID not found"), "{ \"message\": \"ID not found\" }", http.StatusNotFound} } - guestID, err := getID(request) - if err != nil { - return &appError{err, "{ \"message\": \"Failed to parse ID\" }", - http.StatusInternalServerError} - } - err = handler.store.Delete(int(guestID)) + guestId := getId(request) + err = handler.store.Delete(guestId) if err != nil { return &appError{err, "{ \"message\": \"Failed to get guests\" }", http.StatusInternalServerError} @@ -342,6 +337,6 @@ func (handler *GuestHandler) deleteGuest(request *http.Request) *appError { return nil } -func getID(request *http.Request) (int64, error) { - return strconv.ParseInt(guestIDRegex.FindStringSubmatch(request.URL.Path)[1], 10, 32) +func getId(request *http.Request) string { + return guestIdRegex.FindStringSubmatch(request.URL.Path)[1] } diff --git a/server/guest/models.go b/server/guest/models.go index d93bd53..280ee97 100644 --- a/server/guest/models.go +++ b/server/guest/models.go @@ -3,7 +3,7 @@ package guest import "github.com/golang-jwt/jwt/v5" type Guest struct { - ID int `json:"id"` + Id string `json:"id"` FirstName string `json:"firstName"` LastName string `json:"lastName"` Attendance string `json:"attendance"` diff --git a/server/guest/store.go b/server/guest/store.go index 16123e3..56a8c2c 100644 --- a/server/guest/store.go +++ b/server/guest/store.go @@ -48,7 +48,7 @@ func (store Store) Find(name Name) (Guest, error) { func createGuest(name Name, guestRows pgx.Rows) (Guest, bool) { var guest Guest for guestRows.Next() { - err := guestRows.Scan(&guest.ID, &guest.FirstName, &guest.LastName, + err := guestRows.Scan(&guest.Id, &guest.FirstName, &guest.LastName, &guest.Attendance, &guest.Email, &guest.Message, &guest.PartySize) if err != nil { return Guest{}, false @@ -64,13 +64,13 @@ func createGuest(name Name, guestRows pgx.Rows) (Guest, bool) { func addParty(guestWithoutParty Guest, partyRows pgx.Rows) (Guest, error) { guestWithParty := guestWithoutParty for partyRows.Next() { - var guestID int + var guestId string var partyMember Name - err := partyRows.Scan(&guestID, &partyMember.FirstName, &partyMember.LastName) + err := partyRows.Scan(&guestId, &partyMember.FirstName, &partyMember.LastName) if err != nil { return Guest{}, err } - if guestID == guestWithParty.ID { + if guestId == guestWithParty.Id { guestWithParty.PartyList = append(guestWithParty.PartyList, partyMember) } } @@ -108,7 +108,7 @@ func (store Store) createGuestSlice(guestRows pgx.Rows) ([]Guest, error) { guests := []Guest{} for guestRows.Next() { var guest Guest - err := guestRows.Scan(&guest.ID, &guest.FirstName, &guest.LastName, + err := guestRows.Scan(&guest.Id, &guest.FirstName, &guest.LastName, &guest.Attendance, &guest.Email, &guest.Message, &guest.PartySize) if err != nil { return []Guest{}, err @@ -122,14 +122,14 @@ func addPartySlice(guestsWithoutParty []Guest, partyRows pgx.Rows) ([]Guest, error) { guestsWithParty := guestsWithoutParty for partyRows.Next() { - var guestID int + var guestId string var partyMember Name - err := partyRows.Scan(&guestID, &partyMember.FirstName, &partyMember.LastName) + err := partyRows.Scan(&guestId, &partyMember.FirstName, &partyMember.LastName) if err != nil { return []Guest{}, err } for i, guest := range guestsWithParty { - if guestID == guest.ID { + if guestId == guest.Id { guestsWithParty[i].PartyList = append(guest.PartyList, partyMember) } } @@ -147,7 +147,7 @@ func (store Store) Add(guest Guest) error { func (store Store) insertGuest(guest Guest) error { statement := `insert into guest (id, first_name, last_name, attendance, email, message, party_size) values ($1, $2, $3, $4, $5, $6, $7)` - _, err := store.database.Exec(context.Background(), statement, guest.ID, + _, err := store.database.Exec(context.Background(), statement, guest.Id, guest.FirstName, guest.LastName, guest.Attendance, guest.Email, guest.Message, guest.PartySize) return err @@ -157,7 +157,7 @@ func (store Store) Update(guest Guest) error { if err := store.updateGuest(guest); err != nil { return err } - if err := store.deleteParty(guest.ID); err != nil { + if err := store.deleteParty(guest.Id); err != nil { return err } return store.insertParty(guest) @@ -167,13 +167,13 @@ func (store Store) updateGuest(guest Guest) error { statement := `update guest set attendance = $1, email = $2, message = $3, party_size = $4 where id = $5` _, err := store.database.Exec(context.Background(), statement, - guest.Attendance, guest.Email, guest.Message, guest.PartySize, guest.ID) + guest.Attendance, guest.Email, guest.Message, guest.PartySize, guest.Id) return err } -func (store Store) deleteParty(guestID int) error { +func (store Store) deleteParty(guestId string) error { statement := "delete from party where guest_id = $1" - _, err := store.database.Exec(context.Background(), statement, guestID) + _, err := store.database.Exec(context.Background(), statement, guestId) return err } @@ -181,7 +181,7 @@ func (store Store) insertParty(guest Guest) error { statement := `insert into party (guest_id, first_name, last_name) values ($1, $2, $3)` for _, pg := range guest.PartyList { - _, err := store.database.Exec(context.Background(), statement, guest.ID, + _, err := store.database.Exec(context.Background(), statement, guest.Id, pg.FirstName, pg.LastName) if err != nil { return err @@ -190,18 +190,18 @@ func (store Store) insertParty(guest Guest) error { return nil } -func (store Store) Delete(guestID int) error { - if err := store.deleteGuest(guestID); err != nil { +func (store Store) Delete(guestId string) error { + if err := store.deleteGuest(guestId); err != nil { return err } - if err := store.deleteParty(guestID); err != nil { + if err := store.deleteParty(guestId); err != nil { return err } return nil } -func (store Store) deleteGuest(guestID int) error { +func (store Store) deleteGuest(guestId string) error { statement := "delete from guest where id = $1" - _, err := store.database.Exec(context.Background(), statement, guestID) + _, err := store.database.Exec(context.Background(), statement, guestId) return err } diff --git a/server/schema.sql b/server/schema.sql index 809d8c9..44881c8 100644 --- a/server/schema.sql +++ b/server/schema.sql @@ -1,21 +1,21 @@ create table guest ( - id serial PRIMARY KEY, + id uuid primary key default gen_random_uuid(), first_name varchar(64), last_name varchar(64), - attendance varchar(8) DEFAULT 'no' NOT NULL, + attendance varchar(8) not null, email varchar(64), message varchar(128), party_size integer ); create table party ( - guest_id integer NOT NULL references guest(id) ON DELETE CASCADE, + guest_id uuid not null references guest(id) on delete cascade, first_name varchar(64), last_name varchar(64) ); create table admin ( - id serial PRIMARY KEY, + id uuid primary key default gen_random_uuid(), username varchar(64), password varchar(64) ) \ No newline at end of file diff --git a/server/test/guest_test.go b/server/test/guest_test.go index 490df84..b7d1aae 100644 --- a/server/test/guest_test.go +++ b/server/test/guest_test.go @@ -21,7 +21,7 @@ var ( password = os.Getenv("PASS") host = "localhost" port = "5432" - database = "postgres" + database = "wedding" ) func TestUpdateRSVP(test *testing.T) { @@ -66,13 +66,13 @@ func TestUpdateRSVP(test *testing.T) { func logInGuest(guestHandler *guest.GuestHandler, test *testing.T) guest.Login { response := httptest.NewRecorder() loginRequest, err := http.NewRequest(http.MethodPost, - fmt.Sprintf("http://%s:8080/guests/login", host), strings.NewReader(getName())) + fmt.Sprintf("http://%s:8080/api/guests/login", host), strings.NewReader(getName())) if err != nil { test.Error(err) } guestHandler.ServeHTTP(response, loginRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) var login guest.Login if err = json.NewDecoder(response.Body).Decode(&login); err != nil { log.Fatal(err) @@ -83,24 +83,24 @@ func logInGuest(guestHandler *guest.GuestHandler, test *testing.T) guest.Login { func addPartyMember(guestHandler *guest.GuestHandler, token string, test *testing.T) { response := httptest.NewRecorder() putRequest, err := http.NewRequest(http.MethodPut, - fmt.Sprintf("http://%s:8080/guests/1", host), strings.NewReader(getUpdatedGuest())) + fmt.Sprintf("http://%s:8080/api/guests/1", host), strings.NewReader(getUpdatedGuest())) if err != nil { test.Error(err) } putRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, putRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) } func logInAdmin(adminHandler *admin.AdminHandler, test *testing.T) admin.Login { response := httptest.NewRecorder() loginRequest, err := http.NewRequest(http.MethodPost, - fmt.Sprintf("http://%s:8080/admin/login", host), strings.NewReader(getCredentials())) + fmt.Sprintf("http://%s:8080/api/admin/login", host), strings.NewReader(getCredentials())) if err != nil { test.Error(err) } adminHandler.ServeHTTP(response, loginRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) var login admin.Login if err = json.NewDecoder(response.Body).Decode(&login); err != nil { log.Fatal(err) @@ -111,13 +111,13 @@ func logInAdmin(adminHandler *admin.AdminHandler, test *testing.T) admin.Login { func getGuests(guestHandler *guest.GuestHandler, token string, test *testing.T) []guest.Guest { response := httptest.NewRecorder() getRequest, err := http.NewRequest(http.MethodGet, - fmt.Sprintf("http://%s:8080/guests/", host), nil) + fmt.Sprintf("http://%s:8080/api/guests/", host), nil) if err != nil { test.Error(err) } getRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, getRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) var guests []guest.Guest if err := json.NewDecoder(response.Body).Decode(&guests); err != nil { test.Error(err) @@ -128,18 +128,18 @@ func getGuests(guestHandler *guest.GuestHandler, token string, test *testing.T) func deletePartyMember(guestHandler *guest.GuestHandler, token string, test *testing.T) { response := httptest.NewRecorder() putRequest, err := http.NewRequest(http.MethodPut, - fmt.Sprintf("http://%s:8080/guests/1", host), strings.NewReader(getEmptyGuest())) + fmt.Sprintf("http://%s:8080/api/guests/4095e885-8d1b-4c59-9fee-4d6ef53b86a3", host), strings.NewReader(getEmptyGuest())) if err != nil { test.Error(err) } putRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, putRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) } func findGuest(guests []guest.Guest) *guest.Guest { for _, guest := range guests { - if guest.ID == 1 { + if guest.FirstName == "Michael" { return &guest } } @@ -163,14 +163,16 @@ func getCredentials() string { } func getUpdatedGuest() string { - return `{"id":1,"firstName":"Michael","lastName":"Hunteman", "attendance":"accept", + return `{"id":"4095e885-8d1b-4c59-9fee-4d6ef53b86a3","firstName":"Michael", + "lastName":"Hunteman", "attendance":"accept", "email":"mhunteman@cox.net","message":"We'll be there!","partySize":2, "partyList":[{"firstName":"Madison","lastName":"Rossitto"}]}` } func getEmptyGuest() string { - return `{"id":1,"firstName":"Michael","lastName":"Hunteman","attendance":"", - "email":"","message":"","partySize":1,"partyList":[]}` + return `{"id":"4095e885-8d1b-4c59-9fee-4d6ef53b86a3","firstName":"Michael", + "lastName":"Hunteman","attendance":"", "email":"","message":"", + "partySize":1,"partyList":[]}` } func TestAddGuest(test *testing.T) { @@ -206,25 +208,25 @@ func TestAddGuest(test *testing.T) { func deleteGuest(guestHandler *guest.GuestHandler, token string, test *testing.T) { response := httptest.NewRecorder() deleteRequest, err := http.NewRequest(http.MethodDelete, - fmt.Sprintf("http://%s:8080/guests/1", host), nil) + fmt.Sprintf("http://%s:8080/api/guests/4095e885-8d1b-4c59-9fee-4d6ef53b86a3", host), nil) if err != nil { test.Error(err) } deleteRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, deleteRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) } func postGuest(guestHandler *guest.GuestHandler, token string, test *testing.T) { response := httptest.NewRecorder() putRequest, err := http.NewRequest(http.MethodPost, - fmt.Sprintf("http://%s:8080/guests/", host), strings.NewReader(getEmptyGuest())) + fmt.Sprintf("http://%s:8080/api/guests/", host), strings.NewReader(getEmptyGuest())) if err != nil { test.Error(err) } putRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, putRequest) - assertEquals(test, response.Result().StatusCode, 200) + assertEquals(test, response.Code, 200) } func TestInvalidGuest(test *testing.T) { @@ -244,25 +246,25 @@ func TestInvalidGuest(test *testing.T) { func logInInvalidGuest(guestHandler *guest.GuestHandler, test *testing.T) { response := httptest.NewRecorder() loginRequest, err := http.NewRequest(http.MethodPost, - fmt.Sprintf("http://%s:8080/guests/login", host), strings.NewReader("{}")) + fmt.Sprintf("http://%s:8080/api/guests/login", host), strings.NewReader("{}")) if err != nil { test.Error(err) } guestHandler.ServeHTTP(response, loginRequest) - assertEquals(test, response.Result().StatusCode, 401) + assertEquals(test, response.Code, 401) } func updateInvalidGuest(guestHandler *guest.GuestHandler, token string, test *testing.T) { response := httptest.NewRecorder() putRequest, err := http.NewRequest(http.MethodPut, - fmt.Sprintf("http://%s:8080/guests/1", host), strings.NewReader(getUpdatedGuest())) + fmt.Sprintf("http://%s:8080/api/guests/1", host), strings.NewReader(getUpdatedGuest())) if err != nil { test.Error(err) } putRequest.Header.Set("Authorization", token) guestHandler.ServeHTTP(response, putRequest) - assertEquals(test, response.Result().StatusCode, 400) + assertEquals(test, response.Code, 400) } func TestInvalidAdmin(test *testing.T) { @@ -282,11 +284,11 @@ func TestInvalidAdmin(test *testing.T) { func logInInvalidAdmin(adminHandler *admin.AdminHandler, token string, test *testing.T) { response := httptest.NewRecorder() putRequest, err := http.NewRequest(http.MethodPost, - fmt.Sprintf("http://%s:8080/admin/login", host), strings.NewReader("{}")) + fmt.Sprintf("http://%s:8080/api/admin/login", host), strings.NewReader("{}")) if err != nil { test.Error(err) } putRequest.Header.Set("Authorization", token) adminHandler.ServeHTTP(response, putRequest) - assertEquals(test, response.Result().StatusCode, 401) + assertEquals(test, response.Code, 401) } -- cgit v1.2.3