diff --git a/handlers/handlers.go b/handlers/handlers.go
index c59a045..1e04b41 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -5,6 +5,7 @@ import (
"fmt"
"html/template"
"net/http"
+ "strconv"
"strings"
"time"
@@ -299,8 +300,38 @@ func HandleNoteApiRequest(
fmt.Fprint(responseWriter, string(noteString))
+ case http.MethodDelete:
+
+ id, err := strconv.ParseInt(request.URL.Query().Get("id"), 10, 64)
+ if err != nil {
+ http.Error(responseWriter, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ noteId := models.NoteId(id)
+
+ noteMap, err := env.Db.GetUsersNotes(userId)
+ if err != nil {
+ http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ if _, ok := noteMap[noteId]; !ok {
+ errorString := "No note with that Id written by you was found"
+ http.Error(responseWriter, errorString, http.StatusBadRequest)
+ return
+ }
+
+ err = env.Db.DeleteNoteById(noteId)
+ if err != nil {
+ http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ responseWriter.WriteHeader(http.StatusOK)
+
default:
- respondWithMethodNotAllowed(responseWriter, http.MethodGet, http.MethodPost)
+ respondWithMethodNotAllowed(responseWriter, http.MethodGet, http.MethodPost, http.MethodDelete)
}
}
diff --git a/integration_test.go b/integration_test.go
index 9aa0e8f..b5900c5 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -8,10 +8,15 @@ import (
"net/http"
"net/http/cookiejar"
"net/http/httptest"
+ "strconv"
+ // "net/url"
+ // "io"
+ "io/ioutil"
"path/filepath"
"reflect"
"runtime"
"testing"
+ "time"
"github.com/atmiguel/cerealnotes/handlers"
"github.com/atmiguel/cerealnotes/models"
@@ -145,6 +150,59 @@ func TestAuthenticatedFlow(t *testing.T) {
equals(t, http.StatusCreated, resp.StatusCode)
}
+ // Delete note
+ {
+ mockDb.Func_GetUsersNotes = func(userId models.UserId) (models.NoteMap, error) {
+ return models.NoteMap(map[models.NoteId]*models.Note{
+ models.NoteId(noteIdAsInt): &models.Note{
+ AuthorId: models.UserId(userIdAsInt),
+ Content: content,
+ CreationTime: time.Now(),
+ },
+ }), nil
+ }
+
+ mockDb.Func_DeleteNoteById = func(noteid models.NoteId) error {
+ if int64(noteid) == noteIdAsInt {
+ return nil
+ }
+
+ return errors.New("Somehow you didn't get the correct error")
+ }
+
+ resp, err := sendDeleteRequest(client, server.URL+paths.NoteApi+"?id="+strconv.FormatInt(noteIdAsInt, 10))
+ ok(t, err)
+ // printBody(resp)
+
+ 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) {
+
+ req, err := http.NewRequest("DELETE", myUrl, nil)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return client.Do(req)
+
+}
+
+func printBody(resp *http.Response) {
+ buf, bodyErr := ioutil.ReadAll(resp.Body)
+ if bodyErr != nil {
+ fmt.Print("bodyErr ", bodyErr.Error())
+ return
+ }
+
+ rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
+ rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))
+ fmt.Printf("BODY: %q", rdr1)
+ resp.Body = rdr2
}
// Helpers
@@ -155,6 +213,8 @@ type DiyMockDataStore struct {
Func_StoreNewUser func(string, *models.EmailAddress, string) error
Func_AuthenticateUserCredentials func(*models.EmailAddress, string) error
Func_GetIdForUserWithEmailAddress func(*models.EmailAddress) (models.UserId, error)
+ Func_GetUsersNotes func(models.UserId) (models.NoteMap, error)
+ Func_DeleteNoteById func(models.NoteId) error
}
func (mock *DiyMockDataStore) StoreNewNote(note *models.Note) (models.NoteId, error) {
@@ -177,6 +237,14 @@ func (mock *DiyMockDataStore) GetIdForUserWithEmailAddress(email *models.EmailAd
return mock.Func_GetIdForUserWithEmailAddress(email)
}
+func (mock *DiyMockDataStore) GetUsersNotes(userId models.UserId) (models.NoteMap, error) {
+ return mock.Func_GetUsersNotes(userId)
+}
+
+func (mock *DiyMockDataStore) DeleteNoteById(noteId models.NoteId) error {
+ return mock.Func_DeleteNoteById(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/migrations/0000_createDbs.sql b/migrations/0000_createDbs.sql
index 9774b6c..f272b35 100644
--- a/migrations/0000_createDbs.sql
+++ b/migrations/0000_createDbs.sql
@@ -19,17 +19,17 @@ CREATE TABLE IF NOT EXISTS publication (
CREATE TABLE IF NOT EXISTS note (
id bigserial PRIMARY KEY,
- author_id bigint references app_user(id) NOT NULL,
+ author_id bigint references app_user(id) ON DELETE CASCADE NOT NULL,
content text NOT NULL,
creation_time timestamp NOT NULL
);
CREATE TABLE IF NOT EXISTS note_to_publication_relationship (
- note_id bigint PRIMARY KEY references note(id),
- publication_id bigint references publication(id) NOT NULL
+ note_id bigint PRIMARY KEY references note(id) ON DELETE CASCADE,
+ publication_id bigint references publication(id) ON DELETE CASCADE NOT NULL
);
CREATE TABLE IF NOT EXISTS note_to_category_relationship (
- note_id bigint PRIMARY KEY references note(id),
- category category_type NOT NULL
+ note_id bigint PRIMARY KEY references note(id) ON DELETE CASCADE,
+ type category_type NOT NULL
);
\ No newline at end of file
diff --git a/models/datastore.go b/models/datastore.go
index 5779ee9..b3b4d3b 100644
--- a/models/datastore.go
+++ b/models/datastore.go
@@ -24,6 +24,8 @@ type Datastore interface {
StoreNewUser(string, *EmailAddress, string) error
AuthenticateUserCredentials(*EmailAddress, string) error
GetIdForUserWithEmailAddress(*EmailAddress) (UserId, error)
+ GetUsersNotes(UserId) (NoteMap, error)
+ DeleteNoteById(NoteId) error
}
type DB struct {
diff --git a/models/note.go b/models/note.go
index 7b3b47e..6fbcb75 100644
--- a/models/note.go
+++ b/models/note.go
@@ -1,6 +1,7 @@
package models
import (
+ "errors"
"time"
)
@@ -12,6 +13,8 @@ type Note struct {
CreationTime time.Time `json:"creationTime"`
}
+var NoNoteFoundError = errors.New("No note with that information could be found")
+
// DB methods
func (db *DB) StoreNewNote(
@@ -33,3 +36,51 @@ func (db *DB) StoreNewNote(
}
return NoteId(noteId), nil
}
+
+func (db *DB) GetUsersNotes(userId UserId) (NoteMap, error) {
+ noteMap := make(map[NoteId]*Note)
+
+ {
+ sqlQuery := `
+ SELECT id, author_id, content, creation_time FROM note
+ WHERE author_id = $1`
+ rows, err := db.Query(sqlQuery, int64(userId))
+ if err != nil {
+ return nil, convertPostgresError(err)
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var tempId int64
+ tempNote := &Note{}
+ if err := rows.Scan(&tempId, &tempNote.AuthorId, &tempNote.Content, &tempNote.CreationTime); err != nil {
+ return nil, convertPostgresError(err)
+ }
+
+ noteMap[NoteId(tempId)] = tempNote
+ }
+ }
+
+ return noteMap, nil
+}
+
+func (db *DB) DeleteNoteById(noteId NoteId) error {
+ sqlQuery := `
+ DELETE FROM note
+ WHERE id = $1`
+
+ num, err := db.execNoResults(sqlQuery, int64(noteId))
+ if err != nil {
+ return err
+ }
+
+ if num == 0 {
+ return NoNoteFoundError
+ }
+
+ if num != 1 {
+ return errors.New("Somewhere we more than 1 note was deleted")
+ }
+
+ return nil
+}
diff --git a/static/js/base.js b/static/js/base.js
new file mode 100644
index 0000000..2143555
--- /dev/null
+++ b/static/js/base.js
@@ -0,0 +1,32 @@
+$(function() {
+ // http://stepansuvorov.com/blog/2014/04/jquery-put-and-delete/
+ jQuery.each( [ "put", "delete" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ url: url,
+ type: method,
+ dataType: type,
+ data: data,
+ success: callback
+ });
+ };
+ });
+
+ jQuery.prototype.getDOM = function() {
+ if (this.length === 1) {
+ return this[0];
+ }
+
+ if (this.length === 0) {
+ throw "jQuery object is empty"
+ }
+ throw "jQuery Object contains more than 1 object";
+ };
+
+});
\ No newline at end of file
diff --git a/templates/base.tmpl b/templates/base.tmpl
index 02de033..6a2d76c 100644
--- a/templates/base.tmpl
+++ b/templates/base.tmpl
@@ -20,6 +20,7 @@
+
{{ block "js" . }} {{ end }}