From 62a717c8065702b71fde857d4d94f116edf34758 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Tue, 29 Sep 2020 09:25:21 -0600 Subject: [PATCH] Get rid of surrogate keys --- back/store.go | 92 ++++++++++++++++++---------------------------- back/store_test.go | 28 +++++++------- front/server.go | 26 ++++++------- todo.txt | 4 +- 4 files changed, 64 insertions(+), 86 deletions(-) diff --git a/back/store.go b/back/store.go index b7e7fa8..1c39450 100644 --- a/back/store.go +++ b/back/store.go @@ -3,7 +3,6 @@ package back import ( "context" "errors" - "fmt" "log" "os" @@ -72,29 +71,24 @@ func (s *Store) Close() error { const schema = ` CREATE TABLE event ( - id INTEGER PRIMARY KEY, - alpha_id TEXT NOT NULL, + id TEXT NOT NULL PRIMARY KEY, admin_code TEXT NOT NULL, name TEXT NOT NULL, description TEXT NOT NULL, earliest_date DATE NOT NULL, - latest_date DATE NOT NULL, - - UNIQUE (alpha_id) + latest_date DATE NOT NULL ); CREATE TABLE response ( - id INTEGER PRIMARY KEY, - alpha_id TEXT NOT NULL, - event_id INTEGER NOT NULL, + id TEXT NOT NULL PRIMARY KEY, + event_id TEXT NOT NULL, guest_name TEXT NOT NULL, - UNIQUE (alpha_id), FOREIGN KEY (event_id) REFERENCES event(id) ); CREATE TABLE response_time ( - response_id INTEGER NOT NULL, + response_id TEXT NOT NULL, date DATE NOT NULL, time INTEGER NOT NULL, @@ -128,19 +122,19 @@ type CreateEventCommand struct { } type CreateEventResult struct { - AlphaID, AdminCode string + EventID, AdminCode string } const dbDateLayout = "2006-01-02" func (s *Store) CreateEvent(ctx context.Context, cmd CreateEventCommand) (result CreateEventResult, err error) { - const alphaIDLength = 10 + const idLength = 10 const adminCodeLength = 10 conn := s.pool.Get(ctx) defer s.pool.Put(conn) - alphaID, err := s.genString(alphaIDLength) + id, err := s.genString(idLength) if err != nil { return } @@ -151,10 +145,10 @@ func (s *Store) CreateEvent(ctx context.Context, cmd CreateEventCommand) (result } const query = ` - INSERT INTO event(alpha_id, admin_code, name, description, earliest_date, latest_date) + INSERT INTO event(id, admin_code, name, description, earliest_date, latest_date) VALUES (?, ?, ?, ?, ?, ?);` err = sqlitex.Exec(conn, query, nil, - alphaID, + id, adminCode, cmd.Name, cmd.Description, @@ -166,12 +160,12 @@ func (s *Store) CreateEvent(ctx context.Context, cmd CreateEventCommand) (result } result.AdminCode = adminCode - result.AlphaID = alphaID + result.EventID = id return } type GetEventMetadataQuery struct { - AlphaID string + EventID string } type GetEventMetadataResult struct { @@ -187,7 +181,7 @@ func (s *Store) GetEventMetadata(ctx context.Context, query GetEventMetadataQuer const dbQuery = ` SELECT name, description, earliest_date, latest_date FROM event - WHERE alpha_id = ?;` + WHERE id = ?;` var result GetEventMetadataResult var found bool err := sqlitex.Exec(conn, dbQuery, @@ -206,7 +200,7 @@ func (s *Store) GetEventMetadata(ctx context.Context, query GetEventMetadataQuer } result.Latest, err = date.Parse(dbDateLayout, latestDateString) return err - }, query.AlphaID) + }, query.EventID) if err != nil { return GetEventMetadataResult{}, err } @@ -217,7 +211,7 @@ func (s *Store) GetEventMetadata(ctx context.Context, query GetEventMetadataQuer } type GetEventResponseSummaryQuery struct { - AlphaID string + EventID string } type GetEventResponseSummaryResult struct { @@ -229,18 +223,16 @@ func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventRespo defer s.pool.Put(conn) var found bool - var eventID int const eventIDQuery = ` - SELECT id + SELECT 1 FROM event - WHERE alpha_id = ?` + WHERE id = ?` var err = sqlitex.Exec(conn, eventIDQuery, func(stmt *sqlite.Stmt) error { found = true - eventID = stmt.ColumnInt(0) return nil - }, query.AlphaID) + }, query.EventID) if err != nil { return GetEventResponseSummaryResult{}, err } @@ -257,7 +249,7 @@ func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventRespo func(stmt *sqlite.Stmt) error { result.TotalResponses = stmt.ColumnInt(0) return nil - }, eventID) + }, query.EventID) if err != nil { return GetEventResponseSummaryResult{}, err } @@ -265,46 +257,37 @@ func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventRespo } type CreateEventResponseCommand struct { - EventAlphaID string - GuestName string - DateHours map[date.Date]map[int]struct{} + EventID string + GuestName string + DateHours map[date.Date]map[int]struct{} } type CreateEventResponseResult struct { - ResponseAlphaID string + ResponseID string } func (s *Store) CreateEventResponse(ctx context.Context, cmd CreateEventResponseCommand) (result CreateEventResponseResult, err error) { - const responseAlphaIDLength = 10 + const responseIDLength = 10 - fmt.Printf("creating response: %+v\n", cmd) conn := s.pool.Get(ctx) defer s.pool.Put(conn) defer sqlitex.Save(conn)(&err) - responseAlphaID, err := s.genString(responseAlphaIDLength) + responseID, err := s.genString(responseIDLength) if err != nil { return } - const responseQuery = ` - INSERT INTO response(event_id, alpha_id, guest_name) - SELECT event.id AS event_id, ? AS alpha_id, ? as guest_name - FROM event - WHERE event.alpha_id = ?;` - err = sqlitex.Exec(conn, responseQuery, nil, responseAlphaID, cmd.GuestName, cmd.EventAlphaID) + const responseQuery = `INSERT INTO response(event_id, id, guest_name) VALUES (?, ?, ?);` + err = sqlitex.Exec(conn, responseQuery, nil, cmd.EventID, responseID, cmd.GuestName) if err != nil { return } - const responseTimeQuery = ` - INSERT INTO response_time(response_id, date, time) - SELECT response.id AS response_id, ? AS date, ? AS time - FROM response - WHERE response.alpha_id = ?;` + const responseTimeQuery = `INSERT INTO response_time(response_id, date, time) VALUES (?, ?, ?);` for d, hs := range cmd.DateHours { for h := range hs { - err = sqlitex.Exec(conn, responseTimeQuery, nil, d.Format(dbDateLayout), h, responseAlphaID) + err = sqlitex.Exec(conn, responseTimeQuery, nil, responseID, d.Format(dbDateLayout), h) if err != nil { return } @@ -312,13 +295,13 @@ func (s *Store) CreateEventResponse(ctx context.Context, cmd CreateEventResponse } return CreateEventResponseResult{ - ResponseAlphaID: responseAlphaID, + ResponseID: responseID, }, nil } type GetEventResponseQuery struct { - EventAlphaID string - ResponseAlphaID string + EventID string + ResponseID string } type GetEventResponseResult struct { @@ -332,20 +315,17 @@ func (s *Store) GetEventResponse(ctx context.Context, query GetEventResponseQuer var found bool var result GetEventResponseResult - var responseID int const responseQuery = ` - SELECT guest_name, response.id + SELECT guest_name FROM response - JOIN event ON response.event_id = event.id - WHERE response.alpha_id = ? AND event.alpha_id = ?;` + WHERE id = ? AND event_id = ?;` var err = sqlitex.Exec(conn, responseQuery, func(stmt *sqlite.Stmt) error { found = true result.GuestName = stmt.ColumnText(0) - responseID = stmt.ColumnInt(1) return nil - }, query.ResponseAlphaID, query.EventAlphaID) + }, query.ResponseID, query.EventID) if err != nil { return GetEventResponseResult{}, err } @@ -369,7 +349,7 @@ func (s *Store) GetEventResponse(ctx context.Context, query GetEventResponseQuer } result.DateHours[d][stmt.ColumnInt(1)] = struct{}{} return nil - }, responseID) + }, query.ResponseID) if err != nil { return GetEventResponseResult{}, err } diff --git a/back/store_test.go b/back/store_test.go index 36c338a..c6cbefe 100644 --- a/back/store_test.go +++ b/back/store_test.go @@ -36,7 +36,7 @@ func TestCreateEvent(t *testing.T) { is.NoErr(err) metadataResult, err := store.GetEventMetadata(context.Background(), back.GetEventMetadataQuery{ - AlphaID: createResult.AlphaID, + EventID: createResult.EventID, }) is.NoErr(err) is.Equal(metadataResult.Name, name) @@ -58,12 +58,12 @@ func TestGetEventResponseSummary(t *testing.T) { Latest: date.Today().Add(1), }) is.NoErr(err) - return event.AlphaID + return event.EventID } getTotalResponses := func(is *is.I, eventID string) int { responses, err := store.GetEventResponseSummary(context.Background(), back.GetEventResponseSummaryQuery{ - AlphaID: eventID, + EventID: eventID, }) is.NoErr(err) return responses.TotalResponses @@ -71,7 +71,7 @@ func TestGetEventResponseSummary(t *testing.T) { createEmptyResponse := func(is *is.I, eventID string) { _, err = store.CreateEventResponse(context.Background(), back.CreateEventResponseCommand{ - EventAlphaID: eventID, + EventID: eventID, }) is.NoErr(err) } @@ -117,7 +117,7 @@ func TestCreateEventResponse(t *testing.T) { Latest: date.Today().Add(1), }) is.NoErr(err) - return event.AlphaID + return event.EventID } t.Run("saves GuestName", func(t *testing.T) { @@ -126,14 +126,14 @@ func TestCreateEventResponse(t *testing.T) { const guestName = "Etaoin Shrdlu" createResult, err := store.CreateEventResponse(context.Background(), back.CreateEventResponseCommand{ - EventAlphaID: eventID, - GuestName: guestName, + EventID: eventID, + GuestName: guestName, }) is.NoErr(err) getResult, err := store.GetEventResponse(context.Background(), back.GetEventResponseQuery{ - EventAlphaID: eventID, - ResponseAlphaID: createResult.ResponseAlphaID, + EventID: eventID, + ResponseID: createResult.ResponseID, }) is.NoErr(err) is.Equal(getResult.GuestName, guestName) @@ -157,15 +157,15 @@ func TestCreateEventResponse(t *testing.T) { latest: {2: {}, 4: {}, 6: {}}, } createResponseResult, err := store.CreateEventResponse(context.Background(), back.CreateEventResponseCommand{ - EventAlphaID: event.AlphaID, - GuestName: "Etaoin Shrdlu", - DateHours: dateHours, + EventID: event.EventID, + GuestName: "Etaoin Shrdlu", + DateHours: dateHours, }) is.NoErr(err) response, err := store.GetEventResponse(context.Background(), back.GetEventResponseQuery{ - EventAlphaID: event.AlphaID, - ResponseAlphaID: createResponseResult.ResponseAlphaID, + EventID: event.EventID, + ResponseID: createResponseResult.ResponseID, }) is.NoErr(err) is.Equal(dateHours, response.DateHours) diff --git a/front/server.go b/front/server.go index 60febf0..a6ccaaf 100644 --- a/front/server.go +++ b/front/server.go @@ -154,7 +154,7 @@ func internalServerError(w http.ResponseWriter, err error) bool { func (h *handler) handleVote(w http.ResponseWriter, r *http.Request) { eventAlphaID := r.URL.Query().Get(fieldNameEventID) event, err := h.store.GetEventMetadata(context.Background(), back.GetEventMetadataQuery{ - AlphaID: eventAlphaID, + EventID: eventAlphaID, }) if notFound(w, err, "Event not found") || internalServerError(w, err) { return @@ -181,9 +181,9 @@ func (h *handler) handleDoVote(w http.ResponseWriter, r *http.Request) { fmt.Printf("received form: %+v\n", r.Form) cmd := back.CreateEventResponseCommand{ - EventAlphaID: r.Form.Get(fieldNameEventID), - GuestName: r.Form.Get(fieldNameGuestName), - DateHours: make(map[date.Date]map[int]struct{}), + EventID: r.Form.Get(fieldNameEventID), + GuestName: r.Form.Get(fieldNameGuestName), + DateHours: make(map[date.Date]map[int]struct{}), } for _, dateHourString := range r.Form[fieldNameDateHour] { var dateString string @@ -207,22 +207,22 @@ func (h *handler) handleDoVote(w http.ResponseWriter, r *http.Request) { } var successQuery = make(url.Values) - successQuery.Add(fieldNameEventID, cmd.EventAlphaID) - successQuery.Add(fieldNameResponseID, eventResponse.ResponseAlphaID) + successQuery.Add(fieldNameEventID, cmd.EventID) + successQuery.Add(fieldNameResponseID, eventResponse.ResponseID) http.Redirect(w, r, pathVoteSuccess+"?"+successQuery.Encode(), http.StatusSeeOther) } func (h *handler) handleVoteSuccess(w http.ResponseWriter, r *http.Request) { eventResponse, err := h.store.GetEventResponse(r.Context(), back.GetEventResponseQuery{ - EventAlphaID: r.URL.Query().Get(fieldNameEventID), - ResponseAlphaID: r.URL.Query().Get(fieldNameResponseID), + EventID: r.URL.Query().Get(fieldNameEventID), + ResponseID: r.URL.Query().Get(fieldNameResponseID), }) if notFound(w, err, "Event response not found.") || internalServerError(w, err) { return } event, err := h.store.GetEventMetadata(r.Context(), back.GetEventMetadataQuery{ - AlphaID: r.URL.Query().Get(fieldNameEventID), + EventID: r.URL.Query().Get(fieldNameEventID), }) if notFound(w, err, "Event not found") || internalServerError(w, err) { return @@ -295,7 +295,7 @@ func (h *handler) handleDoCreate(w http.ResponseWriter, r *http.Request) { } var successQuery = make(url.Values) - successQuery.Add(fieldNameEventID, event.AlphaID) + successQuery.Add(fieldNameEventID, event.EventID) successQuery.Add(fieldNameAdminCode, event.AdminCode) http.Redirect(w, r, pathCreateSuccess+"?"+successQuery.Encode(), http.StatusSeeOther) } @@ -303,7 +303,7 @@ func (h *handler) handleDoCreate(w http.ResponseWriter, r *http.Request) { func (h *handler) handleCreateSuccess(w http.ResponseWriter, r *http.Request) { eventID := r.URL.Query().Get(fieldNameEventID) event, err := h.store.GetEventMetadata(r.Context(), back.GetEventMetadataQuery{ - AlphaID: eventID, + EventID: eventID, }) if notFound(w, err, "Event not found") || internalServerError(w, err) { return @@ -351,14 +351,14 @@ func (h *handler) handleAdmin(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() eventID := query.Get(fieldNameEventID) metadata, err := h.store.GetEventMetadata(context.Background(), back.GetEventMetadataQuery{ - AlphaID: eventID, + EventID: eventID, }) if notFound(w, err, "Event not found") || internalServerError(w, err) { return } responses, err := h.store.GetEventResponseSummary(context.Background(), back.GetEventResponseSummaryQuery{ - AlphaID: eventID, + EventID: eventID, }) if err != nil { fmt.Fprint(w, err) diff --git a/todo.txt b/todo.txt index efc7d77..3ec6d04 100644 --- a/todo.txt +++ b/todo.txt @@ -13,10 +13,7 @@ Consider redirecting from /create/do to admin Cleanup: ------------ -Make the sqlite pool size configurable -Get rid of the surrogate keys Give response (and therefore response_time) a composite key -Rename all of the alpha ID nonsense Get rid of the Stat call in NewStore. I can't remember the details. I think the old version of sqlitex was panicking if I failed to open a DB file. @@ -25,6 +22,7 @@ Get rid of the Stat call in NewStore. Add optional subtitles to (*front.handler).writePage Take a Page struct with title, subtitle, and contents. Make titles optional in (*front.handler).writePage +Make the sqlite pool size configurable More features: