diff --git a/databaseutil/databaseutil.go b/databaseutil/databaseutil.go index 08f51d2..d4a3ed1 100644 --- a/databaseutil/databaseutil.go +++ b/databaseutil/databaseutil.go @@ -94,40 +94,216 @@ func GetPasswordForUserWithEmailAddress(emailAddress string) ([]byte, error) { return password, nil } -func GetNote(id int64) (int64, int64, string, time.Time, error) { +func GetIdForUserWithEmailAddress(emailAddress string) (int64, error) { + sqlQuery := ` + SELECT id FROM app_user + WHERE email_address = $1` + rows, err := db.Query(sqlQuery, emailAddress) + if err != nil { + return 0, convertPostgresError(err) + } + defer rows.Close() + + var userId int64 + for rows.Next() { + if userId != 0 { + return 0, QueryResultContainedMultipleRowsError + } + + if err := rows.Scan(&userId); err != nil { + return 0, err + } + } + + if userId == 0 { + return 0, QueryResultContainedNoRowsError + } + + return userId, nil +} + +type UserData struct { + Id int64 + DisplayName string +} + +func GetAllUserData() ([]*UserData, error) { sqlQuery := ` - SELECT id, author_id, content, creation_time FROM note - WHERE id = $1` + SELECT id, display_name FROM app_user` - rows, err := db.Query(sqlQuery, id) + rows, err := db.Query(sqlQuery) if err != nil { - return -1, -1, "", time.Time{}, convertPostgresError(err) + return nil, convertPostgresError(err) } defer rows.Close() - var db_id int64 = -1 - var authorId int64 = -1 - var content string - var creationTime time.Time + var users []*UserData = make([]*UserData, 0, 10) for rows.Next() { - if db_id >= 0 { - return -1, -1, "", time.Time{}, QueryResultContainedMultipleRowsError + user := &UserData{} + if err := rows.Scan(&user.Id, &user.DisplayName); err != nil { + return nil, err } - if err := rows.Scan(&db_id, &authorId, &content, &creationTime); err != nil { - return -1, -1, "", time.Time{}, err + users = append(users, user) + } + + return users, nil +} + +type NoteData struct { + Id int64 + AuthorId int64 + Content string + CreationTime time.Time +} + +type ExtendedNoteData struct { + Id int64 + AuthorId int64 + Content string + CreationTime time.Time + PublicationIssue int64 +} + +func GetMyUnpublishedNotes(userId int64) ([]*NoteData, 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` + + rows, err := db.Query(sqlQuery, userId) + if err != nil { + return nil, convertPostgresError(err) + } + + defer rows.Close() + + notes, err := returnNotes(rows) + if err != nil { + return nil, err + } + + return notes, nil +} + +func GetAllPublishedNotes() ([]*NoteData, error) { + + sqlQuery := ` + SELECT id, author_id, content, creation_time FROM note + INNER JOIN note_to_publication_relationship AS note2pub + ON note.id = note2pub.note_id` + + rows, err := db.Query(sqlQuery) + if err != nil { + return nil, convertPostgresError(err) + } + + defer rows.Close() + + notes, err := returnNotes(rows) + if err != nil { + return nil, err + } + + return notes, nil +} + +func GetAllNotesPublishedInIssueSmallerThanOrEqualTo(publictionIssueNumber int) ([]*ExtendedNoteData, error) { + sqlQuery := ` + 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)` + + // sqlQuery2 := ` + // 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(sqlQuery, publictionIssueNumber) + if err != nil { + return nil, convertPostgresError(err) + } + + defer rows.Close() + + var notes []*NoteData = make([]*NoteData, 0, 10) + + for rows.Next() { + note := &ExtendedNoteData{} + if err := rows.Scan(¬e.Id, ¬e.AuthorId, ¬e.Content, ¬e.CreationTime, ¬e.PublicationIssue); err != nil { + return nil, err } + + notes = append(notes, note) } - if db_id < 0 { - return -1, -1, "", time.Time{}, QueryResultContainedNoRowsError + return notes, nil +} + +func GetNote(id int64) (*NoteData, error) { + + sqlQuery := ` + SELECT id, author_id, content, creation_time FROM note + WHERE id = $1` + + rows, err := db.Query(sqlQuery, id) + if err != nil { + return nil, convertPostgresError(err) } - return db_id, authorId, content, creationTime, nil - // return -1, -1, "", time.Time{}, nil + defer rows.Close() + + notes, err := returnNotes(rows) + if err != nil { + return nil, err + } + + if len(notes) > 1 { + + return nil, QueryResultContainedMultipleRowsError + } + + if len(notes) < 1 { + return nil, QueryResultContainedNoRowsError + } + + return notes[0], nil } func DeleteNote(id int64) error { @@ -201,37 +377,22 @@ func StoreNoteCategoryRelationship(noteId int64, category string) error { return nil } -func GetIdForUserWithEmailAddress(emailAddress string) (int64, error) { - sqlQuery := ` - SELECT id FROM app_user - WHERE email_address = $1` - - rows, err := db.Query(sqlQuery, emailAddress) - if err != nil { - return 0, convertPostgresError(err) - } - defer rows.Close() +// PRIVATE +func returnNotes(rows *sql.Rows) ([]*NoteData, error) { + var notes []*NoteData = make([]*NoteData, 0, 10) - var userId int64 for rows.Next() { - if userId != 0 { - return 0, QueryResultContainedMultipleRowsError - } - - if err := rows.Scan(&userId); err != nil { - return 0, err + note := &NoteData{} + if err := rows.Scan(¬e.Id, ¬e.AuthorId, ¬e.Content, ¬e.CreationTime); err != nil { + return nil, err } - } - if userId == 0 { - return 0, QueryResultContainedNoRowsError + notes = append(notes, note) } - return userId, nil + return notes, nil } -// PRIVATE - func convertPostgresError(err error) error { const uniqueConstraintErrorCode = "23505" diff --git a/handlers/handlers.go b/handlers/handlers.go index 523efc4..9acbfe3 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -110,12 +110,10 @@ func HandleUserApiRequest( return } - user1 := models.User{"Adrian"} - user2 := models.User{"Evan"} - - usersById := map[models.UserId]models.User{ - 1: user1, - 2: user2, + usersById, err := userservice.GetUsersById() + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return } usersByIdJson, err := json.Marshal(usersById) @@ -223,23 +221,24 @@ func HandleNoteApiRequest( ) { switch request.Method { case http.MethodGet: - note1 := &models.Note{ - Id: 1, - AuthorId: 1, - Content: "This is an example note.", - CreationTime: time.Now().Add(-oneWeek).UTC(), - } - note2 := &models.Note{ - Id: 2, - AuthorId: 2, - Content: "What is this site for?", - CreationTime: time.Now().Add(-60 * 12).UTC(), + publishedNotes, err := noteservice.GetAllPublishedNotes() + if err != nil { + http.Error(responseWriter, err.Error(), http.StatusInternalServerError) + return } - notes := [2]*models.Note{note1, note2} + fmt.Println("number of published notes") + fmt.Println(len(publishedNotes)) + + myUnpublishedNotes, err := noteservice.GetMyUnpublishedNotes(userId) + + fmt.Println("number of unpublished notes") + fmt.Println(len(myUnpublishedNotes)) + + allNotes := append(publishedNotes, myUnpublishedNotes...) - notesInJson, err := json.Marshal(notes) + notesInJson, err := json.Marshal(allNotes) if err != nil { http.Error(responseWriter, err.Error(), http.StatusInternalServerError) return diff --git a/services/noteservice/noteservice.go b/services/noteservice/noteservice.go index 62c7bfc..c3c05bc 100644 --- a/services/noteservice/noteservice.go +++ b/services/noteservice/noteservice.go @@ -30,20 +30,56 @@ func StoreNewNote( return nil } -func GetNoteById(id int64) (*models.Note, error) { - db_id, authorid, content, creationTime, err := databaseutil.GetNote(id) +func GetAllPublishedNotes() ([]*models.Note, error) { + noteData, err := databaseutil.GetAllPublishedNotes() if err != nil { return nil, err } + var notes []*models.Note = make([]*models.Note, len(noteData), len(noteData)) + + for index, noteDatum := range noteData { + notes[index] = noteDateToNote(noteDatum) + } + + return notes, nil +} + +func noteDateToNote(noteDatum *databaseutil.NoteData) *models.Note { return &models.Note{ - Id: db_id, - AuthorId: models.UserId(authorid), - Content: content, - CreationTime: creationTime, - }, - nil + Id: noteDatum.Id, + AuthorId: models.UserId(noteDatum.AuthorId), + Content: noteDatum.Content, + CreationTime: noteDatum.CreationTime, + } + +} + +func GetMyUnpublishedNotes(userId models.UserId) ([]*models.Note, error) { + + noteData, err := databaseutil.GetMyUnpublishedNotes(int64(userId)) + if err != nil { + return nil, err + } + + var notes []*models.Note = make([]*models.Note, len(noteData), len(noteData)) + + for index, noteDatum := range noteData { + notes[index] = noteDateToNote(noteDatum) + } + + return notes, nil +} + +func GetNoteById(id int64) (*models.Note, error) { + noteData, err := databaseutil.GetNote(id) + + if err != nil { + return nil, err + } + + return noteDateToNote(noteData), nil } func DeleteNoteById(id int64) error { diff --git a/services/userservice/userservice.go b/services/userservice/userservice.go index fab7356..cf2e2a6 100644 --- a/services/userservice/userservice.go +++ b/services/userservice/userservice.go @@ -46,6 +46,20 @@ func StoreNewUser( return nil } +func GetUsersById() (map[models.UserId]*models.User, error) { + userData, err := databaseutil.GetAllUserData() + if err != nil { + return nil, err + } + + usersById := make(map[models.UserId]*models.User) + for _, userDatum := range userData { + usersById[models.UserId(userDatum.Id)] = &models.User{DisplayName: userDatum.DisplayName} + } + + return usersById, nil +} + func AuthenticateUserCredentials(emailAddress *models.EmailAddress, password string) error { storedHashedPassword, err := databaseutil.GetPasswordForUserWithEmailAddress(emailAddress.String()) if err != nil {