Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,13 @@ func HandleUserApiRequest(
return
}

user1 := models.User{"Adrian"}
user2 := models.User{"Evan"}

usersById := map[models.UserId]models.User{
1: user1,
2: user2,
usersById, err := env.Db.GetAllUsersById()
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
return
}

usersByIdJson, err := json.Marshal(usersById)
usersByIdJson, err := usersById.ToJson()
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -231,21 +229,30 @@ func HandleNoteApiRequest(
switch request.Method {
case http.MethodGet:

var notesById models.NoteMap = make(map[models.NoteId]*models.Note, 2)

notesById[models.NoteId(1)] = &models.Note{
AuthorId: 1,
Content: "This is an example note.",
CreationTime: time.Now().Add(-oneWeek).UTC(),
publishedNotes, err := env.Db.GetAllPublishedNotesVisibleBy(userId)
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
return
}

notesById[models.NoteId(2)] = &models.Note{
AuthorId: 2,
Content: "What is this site for?",
CreationTime: time.Now().Add(-60 * 12).UTC(),
myUnpublishedNotes, err := env.Db.GetMyUnpublishedNotes(userId)

fmt.Println("number of published notes")
fmt.Println(len(publishedNotes))
fmt.Println("number of unpublished notes")
fmt.Println(len(myUnpublishedNotes))

allNotes := myUnpublishedNotes

// TODO figure out how to surface the publication number
// for publicationNumber, noteMap := range publishedNotes {
for _, noteMap := range publishedNotes {
for id, note := range noteMap {
allNotes[id] = note
}
}

notesInJson, err := notesById.ToJson()
notesInJson, err := allNotes.ToJson()
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
return
Expand Down
50 changes: 48 additions & 2 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,42 @@ func TestAuthenticatedFlow(t *testing.T) {

// Test get notes
{

mockDb.Func_GetMyUnpublishedNotes = func(userId models.UserId) (models.NoteMap, error) {
if userIdAsInt != int64(userId) {
return nil, errors.New("Invalid userId passed in")
}

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_GetAllPublishedNotesVisibleBy = func(userId models.UserId) (map[int64]models.NoteMap, error) {
if userIdAsInt != int64(userId) {
return nil, errors.New("Invalid userId passed in")
}

return map[int64]models.NoteMap{
1: models.NoteMap(map[models.NoteId]*models.Note{
models.NoteId(44): &models.Note{
AuthorId: models.UserId(99),
Content: "another note",
CreationTime: time.Now(),
},
}),
}, nil

}

resp, err := client.Get(server.URL + paths.NoteApi)
ok(t, err)
equals(t, http.StatusOK, resp.StatusCode)

// TODO when we implement a real get notes feature we should enhance this code.
}

// Test Add category
Expand Down Expand Up @@ -215,6 +246,9 @@ type DiyMockDataStore struct {
Func_GetIdForUserWithEmailAddress func(*models.EmailAddress) (models.UserId, error)
Func_GetUsersNotes func(models.UserId) (models.NoteMap, error)
Func_DeleteNoteById func(models.NoteId) error
Func_GetMyUnpublishedNotes func(models.UserId) (models.NoteMap, error)
Func_GetAllUsersById func() (models.UserMap, error)
Func_GetAllPublishedNotesVisibleBy func(models.UserId) (map[int64]models.NoteMap, error)
}

func (mock *DiyMockDataStore) StoreNewNote(note *models.Note) (models.NoteId, error) {
Expand Down Expand Up @@ -245,6 +279,18 @@ func (mock *DiyMockDataStore) DeleteNoteById(noteId models.NoteId) error {
return mock.Func_DeleteNoteById(noteId)
}

func (mock *DiyMockDataStore) GetMyUnpublishedNotes(userId models.UserId) (models.NoteMap, error) {
return mock.Func_GetMyUnpublishedNotes(userId)
}

func (mock *DiyMockDataStore) GetAllUsersById() (models.UserMap, error) {
return mock.Func_GetAllUsersById()
}

func (mock *DiyMockDataStore) GetAllPublishedNotesVisibleBy(userId models.UserId) (map[int64]models.NoteMap, error) {
return mock.Func_GetAllPublishedNotesVisibleBy(userId)
}

// assert fails the test if the condition is false.
func assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
if !condition {
Expand Down
3 changes: 3 additions & 0 deletions models/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type Datastore interface {
GetIdForUserWithEmailAddress(*EmailAddress) (UserId, error)
GetUsersNotes(UserId) (NoteMap, error)
DeleteNoteById(NoteId) error
GetMyUnpublishedNotes(UserId) (NoteMap, error)
GetAllUsersById() (UserMap, error)
GetAllPublishedNotesVisibleBy(UserId) (map[int64]NoteMap, error)
}

type DB struct {
Expand Down
58 changes: 57 additions & 1 deletion models/datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,21 @@ func TestUser(t *testing.T) {
err = db.StoreNewUser(displayName, emailAddress, password)
ok(t, err)

_, err = db.GetIdForUserWithEmailAddress(emailAddress)
id, err := db.GetIdForUserWithEmailAddress(emailAddress)
ok(t, err)

err = db.AuthenticateUserCredentials(emailAddress, password)
ok(t, err)

userMap, err := db.GetAllUsersById()
ok(t, err)

equals(t, 1, len(userMap))

user, isOk := userMap[id]
assert(t, isOk, "Expected UserId missing")

equals(t, displayName, user.DisplayName)
}

func TestNote(t *testing.T) {
Expand All @@ -87,6 +97,52 @@ func TestNote(t *testing.T) {
id, err := db.StoreNewNote(note)
ok(t, err)
assert(t, int64(id) > 0, "Note Id was not a valid index: "+strconv.Itoa(int(id)))

notemap, err := db.GetMyUnpublishedNotes(userId)
ok(t, err)

retrievedNote, isOk := notemap[id]
assert(t, isOk, "Expected NoteId missing")

equals(t, note.AuthorId, retrievedNote.AuthorId)
equals(t, note.Content, retrievedNote.Content)

err = db.DeleteNoteById(id)
ok(t, err)
}

func TestPublication(t *testing.T) {
db, err := models.ConnectToDatabase(postgresUrl)
ok(t, err)
ClearValuesInTable(db, userTable)
ClearValuesInTable(db, noteTable)
ClearValuesInTable(db, publicationTable)
ClearValuesInTable(db, noteToPublicationTable)

displayName := "bob"
password := "aPassword"
emailAddress := models.NewEmailAddress("thisIsMyEmail@gmail.com")

err = db.StoreNewUser(displayName, emailAddress, password)
ok(t, err)

userId, err := db.GetIdForUserWithEmailAddress(emailAddress)
ok(t, err)

note := &models.Note{AuthorId: userId, Content: "I'm a note", CreationTime: time.Now()}
id, err := db.StoreNewNote(note)
ok(t, err)
assert(t, int64(id) > 0, "Note Id was not a valid index: "+strconv.Itoa(int(id)))

fmt.Println(userId)
publicationToNoteMap, err := db.GetAllPublishedNotesVisibleBy(userId)
ok(t, err)

equals(t, 0, len(publicationToNoteMap))

// TODO once we implement publication publishing, test publication adding,
// and that GetAllPublishedNotesVisibleBy has non-zero rows

}

func TestCategory(t *testing.T) {
Expand Down
139 changes: 122 additions & 17 deletions models/note.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,132 @@ func (db *DB) StoreNewNote(
}

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`

{
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)
noteMap, err := db.getNoteMap(sqlQuery, int64(userId))
if err != nil {
return nil, err
}

return noteMap, nil
}

func (db *DB) GetAllPublishedNotesVisibleBy(userId UserId) (map[int64]NoteMap, error) {

sqlQueryIssueNumber := `
SELECT COUNT(*) AS IssueNumber FROM publication
WHERE publication.author_id = $1`

var publictionIssueNumber int64
if err := db.execOneResult(sqlQueryIssueNumber, &publictionIssueNumber, int64(userId)); err != nil {
return nil, err
}

sqlQueryGetNotes := `
SELECT
note.id,
note.author_id,
note.content,
note.creation_time,
filtered_pubs.rank AS publication_issue
FROM (SELECT *,
Rank()
OVER(
partition BY pub.author_id
ORDER BY pub.creation_time)
FROM publication AS pub) filtered_pubs
INNER JOIN note_to_publication_relationship AS note2pub
ON note2pub.publication_id = filtered_pubs.id
INNER JOIN note
ON note.id = note2pub.note_id
WHERE rank <= ($1)`

// sqlQueryGetNotes := `
// SELECT
// note.id,
// note.author_id,
// note.content,
// note.creation_time,
// note2cat.type AS category,
// filtered_pubs.rank AS publication_issue
// FROM (SELECT *,
// Rank()
// OVER(
// partition BY pub.author_id
// ORDER BY pub.creation_time)
// FROM publication AS pub) filtered_pubs
// INNER JOIN note_to_publication_relationship AS note2pub
// ON note2pub.publication_id = filtered_pubs.id
// INNER JOIN note
// ON note.id = note2pub.note_id
// LEFT OUTER JOIN note_to_category_relationship AS note2cat
// ON note.id = note2cat.note_id
// WHERE rank <= ($1)`

rows, err := db.Query(sqlQueryGetNotes, publictionIssueNumber)
if err != nil {
return nil, convertPostgresError(err)
}

defer rows.Close()

pubToNoteMap := make(map[int64]NoteMap)

for rows.Next() {
var publicationNumber int64
var noteId int64
note := &Note{}
if err := rows.Scan(&noteId, &note.AuthorId, &note.Content, &note.CreationTime, &publicationNumber); err != nil {
return nil, err
}

noteMap, ok := pubToNoteMap[publicationNumber]
if !ok {
pubToNoteMap[publicationNumber] = make(map[NoteId]*Note)
}
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(noteId)] = note

noteMap[NoteId(tempId)] = tempNote
}

return pubToNoteMap, nil
}

func (db *DB) GetMyUnpublishedNotes(userId UserId) (NoteMap, error) {
sqlQuery := `
SELECT id, author_id, content, creation_time FROM note
LEFT OUTER JOIN note_to_publication_relationship AS note2pub
ON note.id = note2pub.note_id
WHERE note2pub.note_id is NULL AND note.author_id = $1`

noteMap, err := db.getNoteMap(sqlQuery, int64(userId))
if err != nil {
return nil, err
}

return noteMap, nil
}

func (db *DB) getNoteMap(sqlQuery string, args ...interface{}) (NoteMap, error) {

noteMap := make(map[NoteId]*Note)

rows, err := db.Query(sqlQuery, args...)
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
Expand All @@ -79,7 +184,7 @@ func (db *DB) DeleteNoteById(noteId NoteId) error {
}

if num != 1 {
return errors.New("Somewhere we more than 1 note was deleted")
return errors.New("somehow more than 1 note was deleted")
}

return nil
Expand Down
Loading