From 6dd1a863d37391868b7712b259386ff90838f1a7 Mon Sep 17 00:00:00 2001 From: G Date: Wed, 19 Sep 2018 09:19:53 -0700 Subject: [PATCH 1/6] edit api & tested --- handlers/handlers.go | 48 +++++++++++++++++++++++++++++- integration_test.go | 64 +++++++++++++++++++++++++++++++++++----- models/databasehelper.go | 2 ++ models/datastore.go | 2 ++ models/datastore_test.go | 9 ++++++ models/note.go | 40 +++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 9 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index d0d31ad..f313671 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -306,7 +306,7 @@ func HandleNoteApiRequest( note := &models.Note{ AuthorId: models.UserId(userId), - Content: noteForm.Content, + Content: strings.TrimSpace(noteForm.Content), CreationTime: time.Now().UTC(), } @@ -331,6 +331,52 @@ func HandleNoteApiRequest( fmt.Fprint(responseWriter, string(noteString)) + case http.MethodPut: + type NoteForm struct { + Id int64 `json:"id"` + Content string `json:"content"` + } + + noteForm := new(NoteForm) + if err := json.NewDecoder(request.Body).Decode(noteForm); err != nil { + http.Error(responseWriter, err.Error(), http.StatusBadRequest) + return + } + + if noteForm.Id < 1 { + http.Error(responseWriter, "Invalid Note Id", http.StatusBadRequest) + return + } + + noteId := models.NoteId(noteForm.Id) + note, err := env.Db.GetNoteById(noteId) + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + if note.AuthorId != userId { + http.Error(responseWriter, "You can only edit notes of which you are the author", http.StatusUnauthorized) + return + } + + content := strings.TrimSpace(noteForm.Content) + if len(content) == 0 { + http.Error(responseWriter, "Note content cannot be empty or just whitespace", http.StatusBadRequest) + return + } + + if content == note.Content { + http.Error(responseWriter, "Note content is the same as existing content", http.StatusBadRequest) + return + } + + if err := env.Db.UpdateNoteContent(noteId, content); err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + responseWriter.WriteHeader(http.StatusOK) case http.MethodDelete: id, err := strconv.ParseInt(request.URL.Query().Get("id"), 10, 64) diff --git a/integration_test.go b/integration_test.go index 3d4ccf2..492e1fb 100644 --- a/integration_test.go +++ b/integration_test.go @@ -5,16 +5,15 @@ import ( "encoding/json" "errors" "fmt" + "io" + "io/ioutil" "net/http" "net/http/cookiejar" "net/http/httptest" - "strconv" - // "net/url" - // "io" - "io/ioutil" "path/filepath" "reflect" "runtime" + "strconv" "testing" "time" @@ -126,7 +125,7 @@ func TestAuthenticatedFlow(t *testing.T) { models.NoteId(noteIdAsInt): &models.Note{ AuthorId: models.UserId(userIdAsInt), Content: content, - CreationTime: time.Now(), + CreationTime: time.Now().UTC(), }, }), nil @@ -187,11 +186,43 @@ func TestAuthenticatedFlow(t *testing.T) { } // publish new api resp, err := client.Post(server.URL+paths.PublicationApi, "", nil) - printBody(resp) ok(t, err) equals(t, http.StatusCreated, resp.StatusCode) } + // Test edit notes + { + type NoteUpdateForm struct { + NoteId int64 `json:"id"` + Content string `json:"content"` + } + + mockDb.Func_GetNoteById = func(models.NoteId) (*models.Note, error) { + return &models.Note{ + AuthorId: models.UserId(userIdAsInt), + Content: content, + CreationTime: time.Now().UTC(), + }, nil + } + + mockDb.Func_UpdateNoteContent = func(models.NoteId, string) error { + return nil + } + + noteForm := &NoteUpdateForm{ + NoteId: 3, + Content: "anything else", + } + + jsonValue, _ := json.Marshal(noteForm) + + resp, err := sendPutRequest(client, server.URL+paths.NoteApi, "application/json", bytes.NewBuffer(jsonValue)) + + ok(t, err) + equals(t, http.StatusOK, resp.StatusCode) + + } + // Delete note { mockDb.Func_GetUsersNotes = func(userId models.UserId) (models.NoteMap, error) { @@ -214,8 +245,6 @@ func TestAuthenticatedFlow(t *testing.T) { resp, err := sendDeleteRequest(client, server.URL+paths.NoteApi+"?id="+strconv.FormatInt(noteIdAsInt, 10)) ok(t, err) - // printBody(resp) - equals(t, http.StatusOK, resp.StatusCode) } @@ -231,7 +260,17 @@ func sendDeleteRequest(client *http.Client, myUrl string) (resp *http.Response, } return client.Do(req) +} + +func sendPutRequest(client *http.Client, myUrl string, contentType string, body io.Reader) (resp *http.Response, err error) { + req, err := http.NewRequest("PUT", myUrl, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", contentType) + return client.Do(req) } func printBody(resp *http.Response) { @@ -262,6 +301,8 @@ type DiyMockDataStore struct { Func_GetAllPublishedNotesVisibleBy func(models.UserId) (map[int64]models.NoteMap, error) Func_PublishNotes func(models.UserId) error Func_StoreNewPublication func(*models.Publication) (models.PublicationId, error) + Func_GetNoteById func(models.NoteId) (*models.Note, error) + Func_UpdateNoteContent func(models.NoteId, string) error } func (mock *DiyMockDataStore) StoreNewNote(note *models.Note) (models.NoteId, error) { @@ -312,6 +353,13 @@ func (mock *DiyMockDataStore) StoreNewPublication(publication *models.Publicatio return mock.Func_StoreNewPublication(publication) } +func (mock *DiyMockDataStore) GetNoteById(noteId models.NoteId) (*models.Note, error) { + return mock.Func_GetNoteById(noteId) +} +func (mock *DiyMockDataStore) UpdateNoteContent(noteId models.NoteId, content string) error { + return mock.Func_UpdateNoteContent(noteId, content) +} + // assert fails the test if the condition is false. func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { if !condition { diff --git a/models/databasehelper.go b/models/databasehelper.go index c709357..830fc3d 100644 --- a/models/databasehelper.go +++ b/models/databasehelper.go @@ -15,6 +15,8 @@ var QueryResultContainedMultipleRowsError = errors.New("query result unexpectedl // QueryResultContainedNoRowsError is returned when a query unexpectedly returns no rows. var QueryResultContainedNoRowsError = errors.New("query result unexpectedly contained no rows") +var WrongNumberOfRowsAffectedError = errors.New("The number of rows affected by the query was more/or less than would make sense") + func convertPostgresError(err error) error { const uniqueConstraintErrorCode = "23505" diff --git a/models/datastore.go b/models/datastore.go index 9fca1c0..0037bdc 100644 --- a/models/datastore.go +++ b/models/datastore.go @@ -31,6 +31,8 @@ type Datastore interface { GetAllPublishedNotesVisibleBy(UserId) (map[int64]NoteMap, error) PublishNotes(UserId) error StoreNewPublication(*Publication) (PublicationId, error) + GetNoteById(NoteId) (*Note, error) + UpdateNoteContent(NoteId, string) error } type DB struct { diff --git a/models/datastore_test.go b/models/datastore_test.go index 6a9815a..c0685b9 100644 --- a/models/datastore_test.go +++ b/models/datastore_test.go @@ -107,6 +107,15 @@ func TestNote(t *testing.T) { equals(t, note.AuthorId, retrievedNote.AuthorId) equals(t, note.Content, retrievedNote.Content) + updatedContent := "some new coolenss" + err = db.UpdateNoteContent(id, updatedContent) + ok(t, err) + + newNote, err := db.GetNoteById(id) + ok(t, err) + equals(t, updatedContent, newNote.Content) + equals(t, note.AuthorId, newNote.AuthorId) + err = db.DeleteNoteById(id) ok(t, err) } diff --git a/models/note.go b/models/note.go index 986e268..a012826 100644 --- a/models/note.go +++ b/models/note.go @@ -170,6 +170,46 @@ func (db *DB) getNoteMap(sqlQuery string, args ...interface{}) (NoteMap, error) return noteMap, nil } +func (db *DB) GetNoteById(noteId NoteId) (*Note, error) { + + sqlQuery := ` + SELECT * FROM note + WHERE note.id = ($1)` + + noteMap, err := db.getNoteMap(sqlQuery, int64(noteId)) + if err != nil { + return nil, err + } + + note, ok := noteMap[noteId] + if !ok { + return nil, NoNoteFoundError + } + + return note, nil +} + +func (db *DB) UpdateNoteContent(noteId NoteId, content string) error { + sqlQuery := ` + UPDATE note SET content = ($2) + WHERE id = ($1)` + + rowsAffected, err := db.execNoResults(sqlQuery, int64(noteId), content) + if err != nil { + return err + } + + if rowsAffected == 0 { + return NoNoteFoundError + } + + if rowsAffected > 1 { + return WrongNumberOfRowsAffectedError + } + + return nil +} + func (db *DB) DeleteNoteById(noteId NoteId) error { sqlQuery := ` DELETE FROM note From 02dbb11161078bfc661f6eaeabff6fc37acf4310 Mon Sep 17 00:00:00 2001 From: G Date: Wed, 19 Sep 2018 10:17:01 -0700 Subject: [PATCH 2/6] cateogry methods: get, update, delete --- handlers/handlers.go | 49 ++++++++++++++++++++++++++++- integration_test.go | 16 ++++++++++ models/databasehelper.go | 2 +- models/datastore.go | 22 ++++++++++---- models/note.go | 66 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 145 insertions(+), 10 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index f313671..e989fe1 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -447,8 +447,55 @@ func HandleCategoryApiRequest( responseWriter.WriteHeader(http.StatusCreated) + case http.MethodPut: + type CategoryForm struct { + NoteId int64 `json:"noteId"` + Category string `json:"category"` + } + + noteForm := new(CategoryForm) + + if err := json.NewDecoder(request.Body).Decode(noteForm); err != nil { + http.Error(responseWriter, err.Error(), http.StatusBadRequest) + return + } + + category, err := models.DeserializeCategory(strings.ToLower(noteForm.Category)) + + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusBadRequest) + return + } + + if err := env.Db.UpdateNoteCategory(models.NoteId(noteForm.NoteId), category); err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + responseWriter.WriteHeader(http.StatusCreated) + + case http.MethodDelete: + + type DeleteForm struct { + NoteId int64 `json:"noteId"` + } + + deleteForm := new(DeleteForm) + + if err := json.NewDecoder(request.Body).Decode(deleteForm); err != nil { + http.Error(responseWriter, err.Error(), http.StatusBadRequest) + return + } + + if err := env.Db.DeleteNoteCategory(models.NoteId(deleteForm.NoteId)); err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + responseWriter.WriteHeader(http.StatusCreated) + default: - respondWithMethodNotAllowed(responseWriter, http.MethodPost) + respondWithMethodNotAllowed(responseWriter, http.MethodPost, http.MethodPut, http.MethodDelete) } } diff --git a/integration_test.go b/integration_test.go index 492e1fb..43db8f6 100644 --- a/integration_test.go +++ b/integration_test.go @@ -303,6 +303,9 @@ type DiyMockDataStore struct { Func_StoreNewPublication func(*models.Publication) (models.PublicationId, error) Func_GetNoteById func(models.NoteId) (*models.Note, error) Func_UpdateNoteContent func(models.NoteId, string) error + Func_UpdateNoteCategory func(models.NoteId, models.Category) error + Func_DeleteNoteCategory func(models.NoteId) error + Func_GetNoteCategory func(models.NoteId) (models.Category, error) } func (mock *DiyMockDataStore) StoreNewNote(note *models.Note) (models.NoteId, error) { @@ -356,10 +359,23 @@ func (mock *DiyMockDataStore) StoreNewPublication(publication *models.Publicatio func (mock *DiyMockDataStore) GetNoteById(noteId models.NoteId) (*models.Note, error) { return mock.Func_GetNoteById(noteId) } + func (mock *DiyMockDataStore) UpdateNoteContent(noteId models.NoteId, content string) error { return mock.Func_UpdateNoteContent(noteId, content) } +func (mock *DiyMockDataStore) UpdateNoteCategory(noteId models.NoteId, category models.Category) error { + return mock.Func_UpdateNoteCategory(noteId, category) +} + +func (mock *DiyMockDataStore) DeleteNoteCategory(noteId models.NoteId) error { + return mock.Func_DeleteNoteCategory(noteId) +} + +func (mock *DiyMockDataStore) GetNoteCategory(noteId models.NoteId) (models.Category, error) { + return mock.Func_GetNoteCategory(noteId) +} + // assert fails the test if the condition is false. func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { if !condition { diff --git a/models/databasehelper.go b/models/databasehelper.go index 830fc3d..49c35ad 100644 --- a/models/databasehelper.go +++ b/models/databasehelper.go @@ -15,7 +15,7 @@ var QueryResultContainedMultipleRowsError = errors.New("query result unexpectedl // QueryResultContainedNoRowsError is returned when a query unexpectedly returns no rows. var QueryResultContainedNoRowsError = errors.New("query result unexpectedly contained no rows") -var WrongNumberOfRowsAffectedError = errors.New("The number of rows affected by the query was more/or less than would make sense") +var TooManyRowsAffectedError = errors.New("There were too many rows affected by the query") func convertPostgresError(err error) error { const uniqueConstraintErrorCode = "23505" diff --git a/models/datastore.go b/models/datastore.go index 0037bdc..3aaddd8 100644 --- a/models/datastore.go +++ b/models/datastore.go @@ -19,20 +19,30 @@ func ConnectToDatabase(databaseUrl string) (*DB, error) { } type Datastore interface { - StoreNewNote(*Note) (NoteId, error) - StoreNewNoteCategoryRelationship(NoteId, Category) error - StoreNewUser(string, *EmailAddress, string) error + // User Actions AuthenticateUserCredentials(*EmailAddress, string) error GetIdForUserWithEmailAddress(*EmailAddress) (UserId, error) + StoreNewUser(string, *EmailAddress, string) error + GetAllUsersById() (UserMap, error) + + // Cateogry Actions + StoreNewNoteCategoryRelationship(NoteId, Category) error + UpdateNoteCategory(NoteId, Category) error + DeleteNoteCategory(NoteId) error + GetNoteCategory(NoteId) (Category, error) + + // Note Actions GetUsersNotes(UserId) (NoteMap, error) DeleteNoteById(NoteId) error GetMyUnpublishedNotes(UserId) (NoteMap, error) - GetAllUsersById() (UserMap, error) + StoreNewNote(*Note) (NoteId, error) GetAllPublishedNotesVisibleBy(UserId) (map[int64]NoteMap, error) - PublishNotes(UserId) error - StoreNewPublication(*Publication) (PublicationId, error) GetNoteById(NoteId) (*Note, error) UpdateNoteContent(NoteId, string) error + + // Publication Actions + PublishNotes(UserId) error + StoreNewPublication(*Publication) (PublicationId, error) } type DB struct { diff --git a/models/note.go b/models/note.go index a012826..e135e88 100644 --- a/models/note.go +++ b/models/note.go @@ -204,12 +204,74 @@ func (db *DB) UpdateNoteContent(noteId NoteId, content string) error { } if rowsAffected > 1 { - return WrongNumberOfRowsAffectedError + return TooManyRowsAffectedError } return nil } +func (db *DB) UpdateNoteCategory(noteId NoteId, category Category) error { + sqlQuery := ` + INSERT INTO note_to_category_relationship (note_id, type) + VALUES ($1, $2) + ON CONFLICT (note_id) DO UPDATE SET type = ($2)` + + rowsAffected, err := db.execNoResults(sqlQuery, int64(noteId), category.String()) + if err != nil { + return err + } + + if rowsAffected == 0 { + return NoNoteFoundError + } + + if rowsAffected > 1 { + return TooManyRowsAffectedError + } + + return nil +} + +func (db *DB) DeleteNoteCategory(noteId NoteId) error { + sqlQuery := ` + DELETE FROM note_to_category_relationship + WHERE note_id = $1` + + num, err := db.execNoResults(sqlQuery, int64(noteId)) + if err != nil { + return err + } + + if num == 0 { + return NoNoteFoundError + } + + if num != 1 { + return TooManyRowsAffectedError + } + + return nil +} + +func (db *DB) GetNoteCategory(noteId NoteId) (Category, error) { + + sqlQuery := ` + SELECT type FROM note_to_category_relationship + WHERE note_id = $1` + + var categoryString string + if err := db.execOneResult(sqlQuery, &categoryString, int64(noteId)); err != nil { + return 0, err + } + + category, err := DeserializeCategory(categoryString) + if err != nil { + return 0, err + } + + return category, nil +} + func (db *DB) DeleteNoteById(noteId NoteId) error { sqlQuery := ` DELETE FROM note @@ -225,7 +287,7 @@ func (db *DB) DeleteNoteById(noteId NoteId) error { } if num != 1 { - return errors.New("somehow more than 1 note was deleted") + return TooManyRowsAffectedError } return nil From 91396a59ca717be5f53f3d8c4c58353c252c068d Mon Sep 17 00:00:00 2001 From: G Date: Wed, 19 Sep 2018 11:31:20 -0700 Subject: [PATCH 3/6] tested handlers --- handlers/handlers.go | 37 +++++++++++++--- integration_test.go | 101 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index e989fe1..77de575 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -260,10 +260,10 @@ func HandleNoteApiRequest( return } - fmt.Println("number of published notes") - fmt.Println(len(publishedNotes)) - fmt.Println("number of unpublished notes") - fmt.Println(len(myUnpublishedNotes)) + // fmt.Println("number of published notes") + // fmt.Println(len(publishedNotes)) + // fmt.Println("number of unpublished notes") + // fmt.Println(len(myUnpublishedNotes)) allNotes := myUnpublishedNotes @@ -419,6 +419,31 @@ func HandleCategoryApiRequest( userId models.UserId, ) { switch request.Method { + case http.MethodGet: + + id, err := strconv.ParseInt(request.URL.Query().Get("id"), 10, 64) + noteId := models.NoteId(id) + + category, err := env.Db.GetNoteCategory(noteId) + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + type categoryObj struct { + Category string `json:"category"` + } + + jsonValue, err := json.Marshal(&categoryObj{Category: category.String()}) + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return + } + + responseWriter.Header().Set("Content-Type", "application/json") + responseWriter.WriteHeader(http.StatusOK) + + fmt.Fprint(responseWriter, string(jsonValue)) case http.MethodPost: type CategoryForm struct { @@ -472,7 +497,7 @@ func HandleCategoryApiRequest( return } - responseWriter.WriteHeader(http.StatusCreated) + responseWriter.WriteHeader(http.StatusOK) case http.MethodDelete: @@ -492,7 +517,7 @@ func HandleCategoryApiRequest( return } - responseWriter.WriteHeader(http.StatusCreated) + responseWriter.WriteHeader(http.StatusOK) default: respondWithMethodNotAllowed(responseWriter, http.MethodPost, http.MethodPut, http.MethodDelete) diff --git a/integration_test.go b/integration_test.go index 43db8f6..2c59ea5 100644 --- a/integration_test.go +++ b/integration_test.go @@ -153,30 +153,94 @@ func TestAuthenticatedFlow(t *testing.T) { equals(t, http.StatusOK, resp.StatusCode) } - // Test Add category + // Test Category { type CategoryForm struct { NoteId int64 `json:"noteId"` Category string `json:"category"` } - metaCategory := models.META + // Add category + { + metaCategory := models.META - categoryForm := &CategoryForm{NoteId: noteIdAsInt, Category: metaCategory.String()} + categoryForm := &CategoryForm{NoteId: noteIdAsInt, Category: metaCategory.String()} - mockDb.Func_StoreNewNoteCategoryRelationship = func(noteId models.NoteId, cat models.Category) error { - if int64(noteId) == noteIdAsInt && cat == metaCategory { - return nil + mockDb.Func_StoreNewNoteCategoryRelationship = func(noteId models.NoteId, cat models.Category) error { + if int64(noteId) == noteIdAsInt && cat == metaCategory { + return nil + } + + return errors.New("Incorrect Data Arrived") } - return errors.New("Incorrect Data Arrived") + jsonValue, _ := json.Marshal(categoryForm) + + resp, err := client.Post(server.URL+paths.CategoryApi, "application/json", bytes.NewBuffer(jsonValue)) + ok(t, err) + equals(t, http.StatusCreated, resp.StatusCode) + } - jsonValue, _ := json.Marshal(categoryForm) + // Get Cateogry + { - resp, err := client.Post(server.URL+paths.CategoryApi, "application/json", bytes.NewBuffer(jsonValue)) - ok(t, err) - equals(t, http.StatusCreated, resp.StatusCode) + mockDb.Func_GetNoteCategory = func(noteId models.NoteId) (models.Category, error) { + if int64(noteId) == noteIdAsInt { + return models.META, nil + } + + return 0, errors.New("Incorrect data") + } + + resp, err := client.Get(server.URL + paths.NoteApi + "?id=" + strconv.FormatInt(noteIdAsInt, 10)) + ok(t, err) + equals(t, http.StatusOK, resp.StatusCode) + + } + + // Update cateogry + { + questionCateogry := models.QUESTIONS + categoryForm := &CategoryForm{NoteId: noteIdAsInt, Category: questionCateogry.String()} + jsonValue, _ := json.Marshal(categoryForm) + + mockDb.Func_UpdateNoteCategory = func(noteId models.NoteId, cat models.Category) error { + if int64(noteId) == noteIdAsInt && cat == questionCateogry { + return nil + } + + return errors.New("Incorrect Data Arrived") + } + + resp, err := sendPutRequest(client, server.URL+paths.CategoryApi, "application/json", bytes.NewBuffer(jsonValue)) + ok(t, err) + equals(t, http.StatusOK, resp.StatusCode) + + } + + // Delete category + { + type DeleteForm struct { + NoteId int64 `json:"noteId"` + } + + deleteForm := &DeleteForm{NoteId: noteIdAsInt} + jsonValue, _ := json.Marshal(deleteForm) + + mockDb.Func_DeleteNoteCategory = func(noteId models.NoteId) error { + if int64(noteId) == noteIdAsInt { + return nil + } + + return errors.New("Incorrect Data Arrived") + } + + resp, err := sendDeleteRequest(client, server.URL+paths.CategoryApi, "application/json", bytes.NewBuffer(jsonValue)) + ok(t, err) + equals(t, http.StatusOK, resp.StatusCode) + + } } // Test publish notes @@ -243,15 +307,24 @@ func TestAuthenticatedFlow(t *testing.T) { return errors.New("Somehow you didn't get the correct error") } - resp, err := sendDeleteRequest(client, server.URL+paths.NoteApi+"?id="+strconv.FormatInt(noteIdAsInt, 10)) + resp, err := sendDeleteUrl(client, server.URL+paths.NoteApi+"?id="+strconv.FormatInt(noteIdAsInt, 10)) ok(t, err) equals(t, http.StatusOK, resp.StatusCode) } } -// func sendDeleteRequest(client *http.Client, myUrl string, contentType string, body io.Reader) (resp *http.Response, err error) { -func sendDeleteRequest(client *http.Client, myUrl string) (resp *http.Response, err error) { +func sendDeleteRequest(client *http.Client, myUrl string, contentType string, body io.Reader) (resp *http.Response, err error) { + req, err := http.NewRequest("DELETE", myUrl, body) + + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", contentType) + return client.Do(req) +} +func sendDeleteUrl(client *http.Client, myUrl string) (resp *http.Response, err error) { req, err := http.NewRequest("DELETE", myUrl, nil) From 9e977fae39c7b2f1a0b4994ff8bf6ac0ede8828b Mon Sep 17 00:00:00 2001 From: G Date: Wed, 19 Sep 2018 11:38:03 -0700 Subject: [PATCH 4/6] tested db edit note api --- migrations/0000_createDbs.sql | 2 +- models/category.go | 62 +++++++++++++++++++++++++++++++++++ models/datastore_test.go | 18 +++++++++- models/note.go | 62 ----------------------------------- 4 files changed, 80 insertions(+), 64 deletions(-) diff --git a/migrations/0000_createDbs.sql b/migrations/0000_createDbs.sql index f272b35..dbe0204 100644 --- a/migrations/0000_createDbs.sql +++ b/migrations/0000_createDbs.sql @@ -31,5 +31,5 @@ CREATE TABLE IF NOT EXISTS note_to_publication_relationship ( CREATE TABLE IF NOT EXISTS note_to_category_relationship ( note_id bigint PRIMARY KEY references note(id) ON DELETE CASCADE, - type category_type NOT NULL + category category_type NOT NULL ); \ No newline at end of file diff --git a/models/category.go b/models/category.go index 514e36c..e5e8102 100644 --- a/models/category.go +++ b/models/category.go @@ -58,3 +58,65 @@ func (db *DB) StoreNewNoteCategoryRelationship( return nil } + +func (db *DB) GetNoteCategory(noteId NoteId) (Category, error) { + + sqlQuery := ` + SELECT category FROM note_to_category_relationship + WHERE note_id = $1` + + var categoryString string + if err := db.execOneResult(sqlQuery, &categoryString, int64(noteId)); err != nil { + return 0, err + } + + category, err := DeserializeCategory(categoryString) + if err != nil { + return 0, err + } + + return category, nil +} + +func (db *DB) UpdateNoteCategory(noteId NoteId, category Category) error { + sqlQuery := ` + INSERT INTO note_to_category_relationship (note_id, category) + VALUES ($1, $2) + ON CONFLICT (note_id) DO UPDATE SET category = ($2)` + + rowsAffected, err := db.execNoResults(sqlQuery, int64(noteId), category.String()) + if err != nil { + return err + } + + if rowsAffected == 0 { + return NoNoteFoundError + } + + if rowsAffected > 1 { + return TooManyRowsAffectedError + } + + return nil +} + +func (db *DB) DeleteNoteCategory(noteId NoteId) error { + sqlQuery := ` + DELETE FROM note_to_category_relationship + WHERE note_id = $1` + + num, err := db.execNoResults(sqlQuery, int64(noteId)) + if err != nil { + return err + } + + if num == 0 { + return NoNoteFoundError + } + + if num != 1 { + return TooManyRowsAffectedError + } + + return nil +} diff --git a/models/datastore_test.go b/models/datastore_test.go index c0685b9..90dc25a 100644 --- a/models/datastore_test.go +++ b/models/datastore_test.go @@ -176,7 +176,23 @@ func TestCategory(t *testing.T) { noteId, err := db.StoreNewNote(note) ok(t, err) - err = db.StoreNewNoteCategoryRelationship(noteId, models.META) + assignedCategory := models.META + err = db.StoreNewNoteCategoryRelationship(noteId, assignedCategory) + ok(t, err) + + retrievedCategory, err := db.GetNoteCategory(noteId) + ok(t, err) + equals(t, assignedCategory, retrievedCategory) + + newAssignedCategory := models.PREDICTIONS + err = db.UpdateNoteCategory(noteId, newAssignedCategory) + ok(t, err) + + newRetrievedCategory, err := db.GetNoteCategory(noteId) + ok(t, err) + equals(t, newAssignedCategory, newRetrievedCategory) + + err = db.DeleteNoteCategory(noteId) ok(t, err) } diff --git a/models/note.go b/models/note.go index e135e88..7b6d980 100644 --- a/models/note.go +++ b/models/note.go @@ -210,68 +210,6 @@ func (db *DB) UpdateNoteContent(noteId NoteId, content string) error { return nil } -func (db *DB) UpdateNoteCategory(noteId NoteId, category Category) error { - sqlQuery := ` - INSERT INTO note_to_category_relationship (note_id, type) - VALUES ($1, $2) - ON CONFLICT (note_id) DO UPDATE SET type = ($2)` - - rowsAffected, err := db.execNoResults(sqlQuery, int64(noteId), category.String()) - if err != nil { - return err - } - - if rowsAffected == 0 { - return NoNoteFoundError - } - - if rowsAffected > 1 { - return TooManyRowsAffectedError - } - - return nil -} - -func (db *DB) DeleteNoteCategory(noteId NoteId) error { - sqlQuery := ` - DELETE FROM note_to_category_relationship - WHERE note_id = $1` - - num, err := db.execNoResults(sqlQuery, int64(noteId)) - if err != nil { - return err - } - - if num == 0 { - return NoNoteFoundError - } - - if num != 1 { - return TooManyRowsAffectedError - } - - return nil -} - -func (db *DB) GetNoteCategory(noteId NoteId) (Category, error) { - - sqlQuery := ` - SELECT type FROM note_to_category_relationship - WHERE note_id = $1` - - var categoryString string - if err := db.execOneResult(sqlQuery, &categoryString, int64(noteId)); err != nil { - return 0, err - } - - category, err := DeserializeCategory(categoryString) - if err != nil { - return 0, err - } - - return category, nil -} - func (db *DB) DeleteNoteById(noteId NoteId) error { sqlQuery := ` DELETE FROM note From 8e0df99d8c7587ccdec18e703a113ceae6a2d68e Mon Sep 17 00:00:00 2001 From: G Date: Wed, 19 Sep 2018 12:55:43 -0700 Subject: [PATCH 5/6] updated delete to be query params --- handlers/handlers.go | 13 +++++-------- integration_test.go | 10 ++-------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index 77de575..2bb2d79 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -501,18 +501,15 @@ func HandleCategoryApiRequest( case http.MethodDelete: - type DeleteForm struct { - NoteId int64 `json:"noteId"` - } - - deleteForm := new(DeleteForm) - - if err := json.NewDecoder(request.Body).Decode(deleteForm); err != nil { + id, err := strconv.ParseInt(request.URL.Query().Get("id"), 10, 64) + if err != nil { http.Error(responseWriter, err.Error(), http.StatusBadRequest) return } - if err := env.Db.DeleteNoteCategory(models.NoteId(deleteForm.NoteId)); err != nil { + noteId := models.NoteId(id) + + if err := env.Db.DeleteNoteCategory(noteId); err != nil { http.Error(responseWriter, err.Error(), http.StatusInternalServerError) return } diff --git a/integration_test.go b/integration_test.go index 2c59ea5..8dcabba 100644 --- a/integration_test.go +++ b/integration_test.go @@ -221,13 +221,6 @@ func TestAuthenticatedFlow(t *testing.T) { // Delete category { - type DeleteForm struct { - NoteId int64 `json:"noteId"` - } - - deleteForm := &DeleteForm{NoteId: noteIdAsInt} - jsonValue, _ := json.Marshal(deleteForm) - mockDb.Func_DeleteNoteCategory = func(noteId models.NoteId) error { if int64(noteId) == noteIdAsInt { return nil @@ -236,7 +229,8 @@ func TestAuthenticatedFlow(t *testing.T) { return errors.New("Incorrect Data Arrived") } - resp, err := sendDeleteRequest(client, server.URL+paths.CategoryApi, "application/json", bytes.NewBuffer(jsonValue)) + resp, err := sendDeleteUrl(client, server.URL+paths.CategoryApi+"?id="+strconv.FormatInt(noteIdAsInt, 10)) + ok(t, err) equals(t, http.StatusOK, resp.StatusCode) From 4f23d2d7cbb7cc8e44d967fd4d5c0d2b31db9c10 Mon Sep 17 00:00:00 2001 From: G Date: Tue, 25 Sep 2018 10:39:52 -0700 Subject: [PATCH 6/6] renamed get dom --- static/js/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/base.js b/static/js/base.js index 2143555..ef61bb6 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -18,7 +18,7 @@ $(function() { }; }); - jQuery.prototype.getDOM = function() { + jQuery.prototype.getUnderlyingDomElement = function() { if (this.length === 1) { return this[0]; }