From 4d1e6375b91354bd2c92ea61bc5209e7c4a859fe Mon Sep 17 00:00:00 2001 From: karn Date: Sun, 22 Dec 2024 04:39:54 +0530 Subject: [PATCH] make content jsonb and fix list notes api --- db/models.go | 2 +- db/notes.sql.go | 855 ++++++++++++++++-- db/queries/notes.sql | 398 ++++++-- db/schema/note.sql | 2 +- domains/notes/find.go | 68 +- domains/notes/note.go | 2 +- domains/notes/upsert.go | 101 ++- routers/models/note.go | 2 +- routers/notes/get_test.go | 22 +- routers/notes/list_test.go | 14 +- ...t_test.TestGet.valid_note_id.approved.json | 22 +- ...hould_return_200_with_cursor.approved.json | 142 +-- ...return_200_with_custom_limit.approved.json | 170 +--- ...eturn_200_with_default_limit.approved.json | 298 ++---- routers/notes/upsert.go | 4 +- 15 files changed, 1400 insertions(+), 702 deletions(-) diff --git a/db/models.go b/db/models.go index 9455bb6..8b5f382 100644 --- a/db/models.go +++ b/db/models.go @@ -26,7 +26,7 @@ type CollectionNote struct { type Note struct { ID int64 UUID string - Content string + Content []byte Favorite bool Deleted bool Trashed bool diff --git a/db/notes.sql.go b/db/notes.sql.go index 02fc9d4..c173b15 100644 --- a/db/notes.sql.go +++ b/db/notes.sql.go @@ -7,14 +7,374 @@ package db import ( "context" - - "github.com/jackc/pgx/v5/pgtype" ) +const checkNoteExists = `-- name: CheckNoteExists :one +SELECT id +FROM note +WHERE uuid = $1 + AND workspace_id = $2 +` + +type CheckNoteExistsParams struct { + UUID string + WorkspaceID int64 +} + +func (q *Queries) CheckNoteExists(ctx context.Context, arg CheckNoteExistsParams) (int64, error) { + row := q.db.QueryRow(ctx, checkNoteExists, arg.UUID, arg.WorkspaceID) + var id int64 + err := row.Scan(&id) + return id, err +} + +const getInitialNotesCreatedAsc = `-- name: GetInitialNotesCreatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY created ASC, + id ASC +LIMIT $2 +` + +type GetInitialNotesCreatedAscParams struct { + WorkspaceID int64 + Limit int64 +} + +func (q *Queries) GetInitialNotesCreatedAsc(ctx context.Context, arg GetInitialNotesCreatedAscParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getInitialNotesCreatedAsc, arg.WorkspaceID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getInitialNotesCreatedDesc = `-- name: GetInitialNotesCreatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY created DESC, + id DESC +LIMIT $2 +` + +type GetInitialNotesCreatedDescParams struct { + WorkspaceID int64 + Limit int64 +} + +func (q *Queries) GetInitialNotesCreatedDesc(ctx context.Context, arg GetInitialNotesCreatedDescParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getInitialNotesCreatedDesc, arg.WorkspaceID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getInitialNotesUpdatedAsc = `-- name: GetInitialNotesUpdatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY updated ASC, + id ASC +LIMIT $2 +` + +type GetInitialNotesUpdatedAscParams struct { + WorkspaceID int64 + Limit int64 +} + +func (q *Queries) GetInitialNotesUpdatedAsc(ctx context.Context, arg GetInitialNotesUpdatedAscParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getInitialNotesUpdatedAsc, arg.WorkspaceID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getInitialNotesUpdatedDesc = `-- name: GetInitialNotesUpdatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY updated DESC, + id DESC +LIMIT $2 +` + +type GetInitialNotesUpdatedDescParams struct { + WorkspaceID int64 + Limit int64 +} + +func (q *Queries) GetInitialNotesUpdatedDesc(ctx context.Context, arg GetInitialNotesUpdatedDescParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getInitialNotesUpdatedDesc, arg.WorkspaceID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getNoteByUUIDAndWorkspace = `-- name: GetNoteByUUIDAndWorkspace :one -SELECT id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id FROM note WHERE uuid = $1 AND workspace_id = $2 @@ -55,47 +415,55 @@ func (q *Queries) GetNoteByUUIDAndWorkspace(ctx context.Context, arg GetNoteByUU return i, err } -const getNotesWithSortingAndPagination = `-- name: GetNotesWithSortingAndPagination :many -SELECT id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id +const getNotesCreatedAsc = `-- name: GetNotesCreatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id FROM note WHERE workspace_id = $1 AND deleted = FALSE AND trashed = FALSE AND has_content = TRUE AND ( - (created > $3 AND $4 = 'created_asc') - OR (created < $3 AND $4 = 'created_dsc') - OR (updated > $3 AND $4 = 'updated_asc') - OR (updated < $3 AND $4 = 'updated_dsc') - OR (created = $3 AND id > $5) -- Tie-breaker for stability - ) -ORDER BY - CASE - WHEN $4 = 'created_asc' THEN created - WHEN $4 = 'created_dsc' THEN created - WHEN $4 = 'updated_asc' THEN updated - WHEN $4 = 'updated_dsc' THEN updated - END ASC, - id ASC + created > $3 + OR (created = $3 AND id > $4) -- tie-break + ) +ORDER BY created ASC, + id ASC LIMIT $2 ` -type GetNotesWithSortingAndPaginationParams struct { +type GetNotesCreatedAscParams struct { WorkspaceID int64 Limit int64 LastSortValue int64 - SortKey pgtype.Text LastNoteID int64 } -func (q *Queries) GetNotesWithSortingAndPagination(ctx context.Context, arg GetNotesWithSortingAndPaginationParams) ([]Note, error) { - rows, err := q.db.Query(ctx, getNotesWithSortingAndPagination, +func (q *Queries) GetNotesCreatedAsc(ctx context.Context, arg GetNotesCreatedAscParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getNotesCreatedAsc, arg.WorkspaceID, arg.Limit, arg.LastSortValue, - arg.SortKey, arg.LastNoteID, ) if err != nil { @@ -139,11 +507,347 @@ func (q *Queries) GetNotesWithSortingAndPagination(ctx context.Context, arg GetN return items, nil } +const getNotesCreatedDesc = `-- name: GetNotesCreatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + created < $3 + OR (created = $3 AND id < $4) -- tie-break + ) +ORDER BY created DESC, + id DESC +LIMIT $2 +` + +type GetNotesCreatedDescParams struct { + WorkspaceID int64 + Limit int64 + LastSortValue int64 + LastNoteID int64 +} + +func (q *Queries) GetNotesCreatedDesc(ctx context.Context, arg GetNotesCreatedDescParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getNotesCreatedDesc, + arg.WorkspaceID, + arg.Limit, + arg.LastSortValue, + arg.LastNoteID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getNotesUpdatedAsc = `-- name: GetNotesUpdatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + updated > $3 + OR (updated = $3 AND id > $4) + ) +ORDER BY updated ASC, + id ASC +LIMIT $2 +` + +type GetNotesUpdatedAscParams struct { + WorkspaceID int64 + Limit int64 + LastSortValue int64 + LastNoteID int64 +} + +func (q *Queries) GetNotesUpdatedAsc(ctx context.Context, arg GetNotesUpdatedAscParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getNotesUpdatedAsc, + arg.WorkspaceID, + arg.Limit, + arg.LastSortValue, + arg.LastNoteID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getNotesUpdatedDesc = `-- name: GetNotesUpdatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + updated < $3 + OR (updated = $3 AND id < $4) + ) +ORDER BY updated DESC, + id DESC +LIMIT $2 +` + +type GetNotesUpdatedDescParams struct { + WorkspaceID int64 + Limit int64 + LastSortValue int64 + LastNoteID int64 +} + +func (q *Queries) GetNotesUpdatedDesc(ctx context.Context, arg GetNotesUpdatedDescParams) ([]Note, error) { + rows, err := q.db.Query(ctx, getNotesUpdatedDesc, + arg.WorkspaceID, + arg.Limit, + arg.LastSortValue, + arg.LastNoteID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Note + for rows.Next() { + var i Note + if err := rows.Scan( + &i.ID, + &i.UUID, + &i.Content, + &i.Favorite, + &i.Deleted, + &i.Trashed, + &i.HasContent, + &i.HasImages, + &i.HasVideos, + &i.HasOpenTasks, + &i.HasClosedTasks, + &i.HasCode, + &i.HasAudios, + &i.HasLinks, + &i.HasFiles, + &i.HasQuotes, + &i.HasTables, + &i.WorkspaceID, + &i.Created, + &i.Updated, + &i.CreatedByID, + &i.UpdatedByID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertNote = `-- name: InsertNote :one +INSERT INTO note (uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, + has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, + has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) +RETURNING id +` + +type InsertNoteParams struct { + UUID string + Content string + Favorite bool + Deleted bool + Trashed bool + HasContent bool + HasImages bool + HasVideos bool + HasOpenTasks bool + HasClosedTasks bool + HasCode bool + HasAudios bool + HasLinks bool + HasFiles bool + HasQuotes bool + HasTables bool + WorkspaceID int64 + Created int64 + Updated int64 + CreatedByID int64 + UpdatedByID int64 +} + +func (q *Queries) InsertNote(ctx context.Context, arg InsertNoteParams) (int64, error) { + row := q.db.QueryRow(ctx, insertNote, + arg.UUID, + arg.Content, + arg.Favorite, + arg.Deleted, + arg.Trashed, + arg.HasContent, + arg.HasImages, + arg.HasVideos, + arg.HasOpenTasks, + arg.HasClosedTasks, + arg.HasCode, + arg.HasAudios, + arg.HasLinks, + arg.HasFiles, + arg.HasQuotes, + arg.HasTables, + arg.WorkspaceID, + arg.Created, + arg.Updated, + arg.CreatedByID, + arg.UpdatedByID, + ) + var id int64 + err := row.Scan(&id) + return id, err +} + const trashNoteByUUID = `-- name: TrashNoteByUUID :one UPDATE note -SET - trashed = TRUE, - updated = $1, +SET trashed = TRUE, + updated = $1, updated_by_id = $2 WHERE uuid = $3 AND workspace_id = $4 @@ -171,11 +875,10 @@ func (q *Queries) TrashNoteByUUID(ctx context.Context, arg TrashNoteByUUIDParams const trashNotesByUUIDs = `-- name: TrashNotesByUUIDs :exec UPDATE note -SET - trashed = TRUE, - updated = $1, +SET trashed = TRUE, + updated = $1, updated_by_id = $2 -WHERE uuid = ANY($3) +WHERE uuid = ANY ($3) AND workspace_id = $4 ` @@ -196,54 +899,35 @@ func (q *Queries) TrashNotesByUUIDs(ctx context.Context, arg TrashNotesByUUIDsPa return err } -const upsertNote = `-- name: UpsertNote :one -INSERT INTO note ( - id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id -) -VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, - $10, $11, $12, $13, $14, $15, - $16, $17, $18, $19, $20, $21, $22 -) -ON CONFLICT (id) DO UPDATE -SET - uuid = EXCLUDED.uuid, - content = EXCLUDED.content, - favorite = EXCLUDED.favorite, - deleted = EXCLUDED.deleted, - trashed = EXCLUDED.trashed, - has_content = EXCLUDED.has_content, - has_images = EXCLUDED.has_images, - has_videos = EXCLUDED.has_videos, - has_open_tasks = EXCLUDED.has_open_tasks, - has_closed_tasks = EXCLUDED.has_closed_tasks, - has_code = EXCLUDED.has_code, - has_audios = EXCLUDED.has_audios, - has_links = EXCLUDED.has_links, - has_files = EXCLUDED.has_files, - has_quotes = EXCLUDED.has_quotes, - has_tables = EXCLUDED.has_tables, - workspace_id = EXCLUDED.workspace_id, - created = EXCLUDED.created, - updated = EXCLUDED.updated, - created_by_id = EXCLUDED.created_by_id, - updated_by_id = EXCLUDED.updated_by_id -RETURNING id +const updateNote = `-- name: UpdateNote :exec +UPDATE note +SET content = $1, + favorite = $2, + has_content = $3, + has_images = $4, + has_videos = $5, + has_open_tasks = $6, + updated_by_id = $7, + has_closed_tasks = $8, + has_code = $9, + has_audios = $10, + has_links = $11, + has_files = $12, + has_quotes = $13, + has_tables = $14, + workspace_id = $15, + updated = $16 +WHERE uuid = $17 ` -type UpsertNoteParams struct { - ID int64 - UUID string +type UpdateNoteParams struct { Content string Favorite bool - Deleted bool - Trashed bool HasContent bool HasImages bool HasVideos bool HasOpenTasks bool + UpdatedByID int64 HasClosedTasks bool HasCode bool HasAudios bool @@ -252,24 +936,19 @@ type UpsertNoteParams struct { HasQuotes bool HasTables bool WorkspaceID int64 - Created int64 Updated int64 - CreatedByID int64 - UpdatedByID int64 + UUID string } -func (q *Queries) UpsertNote(ctx context.Context, arg UpsertNoteParams) (int64, error) { - row := q.db.QueryRow(ctx, upsertNote, - arg.ID, - arg.UUID, +func (q *Queries) UpdateNote(ctx context.Context, arg UpdateNoteParams) error { + _, err := q.db.Exec(ctx, updateNote, arg.Content, arg.Favorite, - arg.Deleted, - arg.Trashed, arg.HasContent, arg.HasImages, arg.HasVideos, arg.HasOpenTasks, + arg.UpdatedByID, arg.HasClosedTasks, arg.HasCode, arg.HasAudios, @@ -278,12 +957,8 @@ func (q *Queries) UpsertNote(ctx context.Context, arg UpsertNoteParams) (int64, arg.HasQuotes, arg.HasTables, arg.WorkspaceID, - arg.Created, arg.Updated, - arg.CreatedByID, - arg.UpdatedByID, + arg.UUID, ) - var id int64 - err := row.Scan(&id) - return id, err + return err } diff --git a/db/queries/notes.sql b/db/queries/notes.sql index 4ee7c3f..1aab7ed 100644 --- a/db/queries/notes.sql +++ b/db/queries/notes.sql @@ -1,78 +1,67 @@ -- name: GetNoteByUUIDAndWorkspace :one -SELECT id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id FROM note WHERE uuid = $1 AND workspace_id = $2; --- name: GetNotesWithSortingAndPagination :many -SELECT id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id +-- name: CheckNoteExists :one +SELECT id FROM note -WHERE workspace_id = $1 - AND deleted = FALSE - AND trashed = FALSE - AND has_content = TRUE - AND ( - (created > @last_sort_value AND @sort_key = 'created_asc') - OR (created < @last_sort_value AND @sort_key = 'created_dsc') - OR (updated > @last_sort_value AND @sort_key = 'updated_asc') - OR (updated < @last_sort_value AND @sort_key = 'updated_dsc') - OR (created = @last_sort_value AND id > @last_note_id) -- Tie-breaker for stability - ) -ORDER BY - CASE - WHEN @sort_key = 'created_asc' THEN created - WHEN @sort_key = 'created_dsc' THEN created - WHEN @sort_key = 'updated_asc' THEN updated - WHEN @sort_key = 'updated_dsc' THEN updated - END ASC, - id ASC -LIMIT $2; +WHERE uuid = $1 + AND workspace_id = $2; + +-- name: UpdateNote :exec +UPDATE note +SET content = $1, + favorite = $2, + has_content = $3, + has_images = $4, + has_videos = $5, + has_open_tasks = $6, + updated_by_id = $7, + has_closed_tasks = $8, + has_code = $9, + has_audios = $10, + has_links = $11, + has_files = $12, + has_quotes = $13, + has_tables = $14, + workspace_id = $15, + updated = $16 +WHERE uuid = $17; --- name: UpsertNote :one -INSERT INTO note ( - id, uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, - has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, - has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id -) -VALUES ( - @id, @uuid, @content, @favorite, @deleted, @trashed, @has_content, @has_images, @has_videos, - @has_open_tasks, @has_closed_tasks, @has_code, @has_audios, @has_links, @has_files, - @has_quotes, @has_tables, @workspace_id, @created, @updated, @created_by_id, @updated_by_id -) -ON CONFLICT (id) DO UPDATE -SET - uuid = EXCLUDED.uuid, - content = EXCLUDED.content, - favorite = EXCLUDED.favorite, - deleted = EXCLUDED.deleted, - trashed = EXCLUDED.trashed, - has_content = EXCLUDED.has_content, - has_images = EXCLUDED.has_images, - has_videos = EXCLUDED.has_videos, - has_open_tasks = EXCLUDED.has_open_tasks, - has_closed_tasks = EXCLUDED.has_closed_tasks, - has_code = EXCLUDED.has_code, - has_audios = EXCLUDED.has_audios, - has_links = EXCLUDED.has_links, - has_files = EXCLUDED.has_files, - has_quotes = EXCLUDED.has_quotes, - has_tables = EXCLUDED.has_tables, - workspace_id = EXCLUDED.workspace_id, - created = EXCLUDED.created, - updated = EXCLUDED.updated, - created_by_id = EXCLUDED.created_by_id, - updated_by_id = EXCLUDED.updated_by_id +-- name: InsertNote :one +INSERT INTO note (uuid, content, favorite, deleted, trashed, has_content, has_images, has_videos, + has_open_tasks, has_closed_tasks, has_code, has_audios, has_links, has_files, + has_quotes, has_tables, workspace_id, created, updated, created_by_id, updated_by_id) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) RETURNING id; -- name: TrashNoteByUUID :one UPDATE note -SET - trashed = TRUE, - updated = @updated, +SET trashed = TRUE, + updated = @updated, updated_by_id = @updated_by_id WHERE uuid = @uuid AND workspace_id = @workspace_id @@ -80,9 +69,280 @@ RETURNING id; -- name: TrashNotesByUUIDs :exec UPDATE note -SET - trashed = TRUE, - updated = @updated, +SET trashed = TRUE, + updated = @updated, updated_by_id = @updated_by_id -WHERE uuid = ANY(@uuids) - AND workspace_id = @workspace_id; \ No newline at end of file +WHERE uuid = ANY (@uuids) + AND workspace_id = @workspace_id; + +-- name: GetInitialNotesCreatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY created ASC, + id ASC +LIMIT $2; + +-- name: GetNotesCreatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + created > @last_sort_value + OR (created = @last_sort_value AND id > @last_note_id) -- tie-break + ) +ORDER BY created ASC, + id ASC +LIMIT $2; + +-- name: GetInitialNotesCreatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY created DESC, + id DESC +LIMIT $2; + +-- name: GetNotesCreatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + created < @last_sort_value + OR (created = @last_sort_value AND id < @last_note_id) -- tie-break + ) +ORDER BY created DESC, + id DESC +LIMIT $2; + +-- name: GetInitialNotesUpdatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY updated ASC, + id ASC +LIMIT $2; + +-- name: GetNotesUpdatedAsc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + updated > @last_sort_value + OR (updated = @last_sort_value AND id > @last_note_id) + ) +ORDER BY updated ASC, + id ASC +LIMIT $2; + +-- name: GetInitialNotesUpdatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE +ORDER BY updated DESC, + id DESC +LIMIT $2; + +-- name: GetNotesUpdatedDesc :many +SELECT id, + uuid, + content, + favorite, + deleted, + trashed, + has_content, + has_images, + has_videos, + has_open_tasks, + has_closed_tasks, + has_code, + has_audios, + has_links, + has_files, + has_quotes, + has_tables, + workspace_id, + created, + updated, + created_by_id, + updated_by_id +FROM note +WHERE workspace_id = $1 + AND deleted = FALSE + AND trashed = FALSE + AND has_content = TRUE + AND ( + updated < @last_sort_value + OR (updated = @last_sort_value AND id < @last_note_id) + ) +ORDER BY updated DESC, + id DESC +LIMIT $2; \ No newline at end of file diff --git a/db/schema/note.sql b/db/schema/note.sql index bfbb815..42b4efb 100644 --- a/db/schema/note.sql +++ b/db/schema/note.sql @@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS note ( id BIGSERIAL PRIMARY KEY, uuid VARCHAR(100) NOT NULL UNIQUE, - content TEXT NOT NULL, + content JSONB NOT NULL, favorite BOOLEAN NOT NULL DEFAULT FALSE, deleted BOOLEAN NOT NULL DEFAULT FALSE, trashed BOOLEAN NOT NULL DEFAULT FALSE, diff --git a/domains/notes/find.go b/domains/notes/find.go index a558146..5803fd6 100644 --- a/domains/notes/find.go +++ b/domains/notes/find.go @@ -10,8 +10,6 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgtype" - "github.com/karngyan/maek/db" "github.com/karngyan/maek/domains/auth" ) @@ -103,16 +101,62 @@ func FindNotesForWorkspace(ctx context.Context, wid int64, cursor string, limit lastSortValue = 0 } - dbNotes, err := db.Q.GetNotesWithSortingAndPagination(ctx, db.GetNotesWithSortingAndPaginationParams{ - WorkspaceID: wid, - Limit: int64(limit + 1), - LastSortValue: lastSortValue, - SortKey: pgtype.Text{ - String: string(sortKey), - Valid: true, - }, - LastNoteID: lastNoteId, - }) + var dbNotes []db.Note + if lastSortValue > 0 { + switch sortKey { + case SortKeyCreatedAsc: + dbNotes, err = db.Q.GetNotesCreatedAsc(ctx, db.GetNotesCreatedAscParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + LastSortValue: lastSortValue, + LastNoteID: lastNoteId, + }) + case SortKeyUpdatedAsc: + dbNotes, err = db.Q.GetNotesUpdatedAsc(ctx, db.GetNotesUpdatedAscParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + LastSortValue: lastSortValue, + LastNoteID: lastNoteId, + }) + case SortKeyCreatedDsc: + dbNotes, err = db.Q.GetNotesCreatedDesc(ctx, db.GetNotesCreatedDescParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + LastSortValue: lastSortValue, + LastNoteID: lastNoteId, + }) + case SortKeyUpdatedDsc: + dbNotes, err = db.Q.GetNotesUpdatedDesc(ctx, db.GetNotesUpdatedDescParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + LastSortValue: lastSortValue, + LastNoteID: lastNoteId, + }) + } + } else { + switch sortKey { + case SortKeyCreatedAsc: + dbNotes, err = db.Q.GetInitialNotesCreatedAsc(ctx, db.GetInitialNotesCreatedAscParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + }) + case SortKeyUpdatedAsc: + dbNotes, err = db.Q.GetInitialNotesUpdatedAsc(ctx, db.GetInitialNotesUpdatedAscParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + }) + case SortKeyCreatedDsc: + dbNotes, err = db.Q.GetInitialNotesCreatedDesc(ctx, db.GetInitialNotesCreatedDescParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + }) + case SortKeyUpdatedDsc: + dbNotes, err = db.Q.GetInitialNotesUpdatedDesc(ctx, db.GetInitialNotesUpdatedDescParams{ + WorkspaceID: wid, + Limit: int64(limit + 1), + }) + } + } if err != nil { return nil, err } diff --git a/domains/notes/note.go b/domains/notes/note.go index d698ff1..08eb560 100644 --- a/domains/notes/note.go +++ b/domains/notes/note.go @@ -5,7 +5,7 @@ import "github.com/karngyan/maek/db" type Note struct { ID int64 UUID string - Content string + Content []byte Favorite bool Trashed bool Deleted bool diff --git a/domains/notes/upsert.go b/domains/notes/upsert.go index 07244b1..644621e 100644 --- a/domains/notes/upsert.go +++ b/domains/notes/upsert.go @@ -11,7 +11,7 @@ import ( type UpsertNoteRequest struct { UUID string - Content string + Content []byte Favorite bool Created int64 Updated int64 @@ -37,7 +37,7 @@ func UpsertNote(ctx context.Context, req *UpsertNoteRequest) (*Note, error) { return nil, errors.New("uuid is required") } - if req.Content == "" { + if len(req.Content) == 0 { return nil, errors.New("content is required") } @@ -49,17 +49,6 @@ func UpsertNote(ctx context.Context, req *UpsertNoteRequest) (*Note, error) { return nil, errors.New("updated by is required") } - // check if note already exists - existingDBNote, err := db.Q.GetNoteByUUIDAndWorkspace(ctx, db.GetNoteByUUIDAndWorkspaceParams{ - UUID: nuuid, - WorkspaceID: req.WorkspaceID, - }) - if err != nil { - if !errors.Is(err, pgx.ErrNoRows) { - return nil, err - } - } - note := &Note{ UUID: nuuid, Content: req.Content, @@ -83,42 +72,66 @@ func UpsertNote(ctx context.Context, req *UpsertNoteRequest) (*Note, error) { HasTables: req.HasTables, } - if existingDBNote.ID > 0 { - note.ID = existingDBNote.ID - note.Trashed = existingDBNote.Trashed - // don't let client change created and created by once created - note.Created = existingDBNote.Created - note.CreatedByID = existingDBNote.CreatedByID - } + err := db.Tx(ctx, func(ctx context.Context, q *db.Queries) (err error) { + note.ID, err = q.CheckNoteExists(ctx, db.CheckNoteExistsParams{ + UUID: nuuid, + WorkspaceID: req.WorkspaceID, + }) + if err != nil { + if !errors.Is(err, pgx.ErrNoRows) { + return err + } + + note.ID, err = q.InsertNote(ctx, db.InsertNoteParams{ + UUID: nuuid, + Content: string(note.Content), + Favorite: note.Favorite, + Deleted: false, + Trashed: false, + HasContent: note.HasContent, + HasImages: note.HasImages, + HasVideos: note.HasVideos, + HasOpenTasks: note.HasOpenTasks, + HasClosedTasks: note.HasClosedTasks, + HasCode: note.HasCode, + HasAudios: note.HasAudios, + HasLinks: note.HasLinks, + HasFiles: note.HasFiles, + HasQuotes: note.HasQuotes, + HasTables: note.HasTables, + WorkspaceID: note.WorkspaceID, + Created: note.Created, + Updated: note.Updated, + CreatedByID: note.CreatedByID, + UpdatedByID: note.UpdatedByID, + }) + + return err + } - id, err := db.Q.UpsertNote(ctx, db.UpsertNoteParams{ - ID: note.ID, - UUID: note.UUID, - Content: note.Content, - Favorite: note.Favorite, - Deleted: note.Deleted, - Trashed: note.Trashed, - HasContent: note.HasContent, - HasImages: note.HasImages, - HasVideos: note.HasVideos, - HasOpenTasks: note.HasOpenTasks, - HasClosedTasks: note.HasClosedTasks, - HasCode: note.HasCode, - HasAudios: note.HasAudios, - HasLinks: note.HasLinks, - HasFiles: note.HasFiles, - HasQuotes: note.HasQuotes, - HasTables: note.HasTables, - WorkspaceID: note.WorkspaceID, - Created: note.Created, - Updated: note.Updated, - CreatedByID: note.CreatedByID, - UpdatedByID: note.UpdatedByID, + // exists; time to do an update + return q.UpdateNote(ctx, db.UpdateNoteParams{ + Content: string(req.Content), + Favorite: note.Favorite, + HasContent: note.HasContent, + HasImages: note.HasImages, + HasVideos: note.HasVideos, + HasOpenTasks: note.HasOpenTasks, + UpdatedByID: note.UpdatedByID, + HasClosedTasks: note.HasClosedTasks, + HasCode: note.HasCode, + HasAudios: note.HasAudios, + HasLinks: note.HasLinks, + HasFiles: note.HasFiles, + HasQuotes: note.HasQuotes, + HasTables: note.HasTables, + WorkspaceID: note.WorkspaceID, + Updated: note.Updated, + }) }) if err != nil { return nil, err } - note.ID = id return note, nil } diff --git a/routers/models/note.go b/routers/models/note.go index 127651a..c8c4436 100644 --- a/routers/models/note.go +++ b/routers/models/note.go @@ -32,7 +32,7 @@ type Note struct { func ModelForNote(note *notes.Note) (*Note, error) { var content map[string]any - if err := json.Unmarshal([]byte(note.Content), &content); err != nil { + if err := json.Unmarshal(note.Content, &content); err != nil { return nil, err } diff --git a/routers/notes/get_test.go b/routers/notes/get_test.go index e2ef060..5d72bcf 100644 --- a/routers/notes/get_test.go +++ b/routers/notes/get_test.go @@ -2,6 +2,7 @@ package notes_test import ( "context" + "encoding/json" "fmt" "testing" @@ -19,9 +20,14 @@ func TestGet(t *testing.T) { cs := tests.NewClientStateWithUser(t) cs2 := tests.NewClientStateWithUserEmail(t, "john@maek.ai") + cbytes, err := json.Marshal(map[string]any{ + "dom": []any{}, + }) + assert.Nil(t, err) + n, err := notes.UpsertNote(context.Background(), ¬es.UpsertNoteRequest{ UUID: "123", - Content: "{ \"dom\": [] }", + Content: cbytes, Favorite: true, Created: timecop.Now().Unix(), Updated: timecop.Now().Unix(), @@ -33,7 +39,7 @@ func TestGet(t *testing.T) { n2, err := notes.UpsertNote(context.Background(), ¬es.UpsertNoteRequest{ UUID: "321", - Content: "{ \"dom\": [] }", + Content: cbytes, Favorite: true, Created: timecop.Now().Unix(), Updated: timecop.Now().Unix(), @@ -46,31 +52,31 @@ func TestGet(t *testing.T) { var testCases = []struct { name string - noteId string + uuid string workspaceId int64 expectedStatus int }{ { name: "valid note id", - noteId: n.UUID, + uuid: n.UUID, workspaceId: cs.Workspace.ID, expectedStatus: 200, }, { name: "invalid note id", - noteId: "random-note-uuid", + uuid: "random-note-uuid", workspaceId: cs.Workspace.ID, expectedStatus: 404, }, { name: "note id from different workspace", - noteId: n2.UUID, + uuid: n2.UUID, workspaceId: cs.Workspace.ID, expectedStatus: 404, }, { name: "session token used from another user to fetch note", - noteId: n2.UUID, + uuid: n2.UUID, workspaceId: cs2.Workspace.ID, expectedStatus: 401, }, @@ -78,7 +84,7 @@ func TestGet(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - rr, err := cs.Get(fmt.Sprintf("/v1/workspaces/%d/notes/%s", tc.workspaceId, tc.noteId)) + rr, err := cs.Get(fmt.Sprintf("/v1/workspaces/%d/notes/%s", tc.workspaceId, tc.uuid)) assert.Nil(t, err) assert.Equal(t, tc.expectedStatus, rr.Code) diff --git a/routers/notes/list_test.go b/routers/notes/list_test.go index ea66d9b..eb91d2d 100644 --- a/routers/notes/list_test.go +++ b/routers/notes/list_test.go @@ -2,6 +2,7 @@ package notes_test import ( "context" + "encoding/json" "fmt" "testing" @@ -17,14 +18,19 @@ func TestList(t *testing.T) { cs := tests.NewClientStateWithUser(t) + cbytes, err := json.Marshal(map[string]any{ + "dom": []any{}, + }) + assert.Nil(t, err) + i := 1 for i < 10 { _, err := notes.UpsertNote(context.Background(), ¬es.UpsertNoteRequest{ UUID: fmt.Sprintf("rand-uuid-%d", i), - Content: "{ \"foo\": \"bar\" }", + Content: cbytes, Favorite: true, - Created: 1234567890, - Updated: 1234567890, + Created: int64(i), + Updated: int64(2 * i), WorkspaceID: cs.Workspace.ID, CreatedByID: cs.User.ID, UpdatedByID: cs.User.ID, @@ -62,7 +68,7 @@ func TestList(t *testing.T) { }, { name: "should return 200 with cursor", - cursor: "NQ==", // base64 encoded 5 + cursor: "MTA6NQ==", // base64 encoded 10:5 limit: "", expectedStatusCode: 200, }, diff --git a/routers/notes/testdata/get_test.TestGet.valid_note_id.approved.json b/routers/notes/testdata/get_test.TestGet.valid_note_id.approved.json index 678f318..9413098 100644 --- a/routers/notes/testdata/get_test.TestGet.valid_note_id.approved.json +++ b/routers/notes/testdata/get_test.TestGet.valid_note_id.approved.json @@ -4,16 +4,7 @@ "dom": [] }, "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -29,16 +20,7 @@ "id": 1, "trashed": false, "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, + "updatedById": 1, "uuid": "123", "workspaceId": 1 } diff --git a/routers/notes/testdata/list_test.TestList.should_return_200_with_cursor.approved.json b/routers/notes/testdata/list_test.TestList.should_return_200_with_cursor.approved.json index dd4ba14..4a34c63 100644 --- a/routers/notes/testdata/list_test.TestList.should_return_200_with_cursor.approved.json +++ b/routers/notes/testdata/list_test.TestList.should_return_200_with_cursor.approved.json @@ -1,21 +1,24 @@ { - "nextCursor": "NA==", + "authors": [ + { + "created": 1234567890, + "defaultWorkspaceId": 1, + "email": "karn@maek.ai", + "id": 1, + "name": "Karn", + "role": "admin", + "updated": 1234567890, + "verified": false + } + ], + "nextCursor": "", "notes": [ { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 4, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -28,37 +31,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 1, + "id": 4, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-1", + "updated": 8, + "updatedById": 1, + "uuid": "rand-uuid-4", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 3, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -71,37 +56,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 2, + "id": 3, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-2", + "updated": 6, + "updatedById": 1, + "uuid": "rand-uuid-3", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 2, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -114,37 +81,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 3, + "id": 2, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-3", + "updated": 4, + "updatedById": 1, + "uuid": "rand-uuid-2", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 1, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -157,20 +106,11 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 4, + "id": 1, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-4", + "updated": 2, + "updatedById": 1, + "uuid": "rand-uuid-1", "workspaceId": 1 } ] diff --git a/routers/notes/testdata/list_test.TestList.should_return_200_with_custom_limit.approved.json b/routers/notes/testdata/list_test.TestList.should_return_200_with_custom_limit.approved.json index adc520a..20e55cd 100644 --- a/routers/notes/testdata/list_test.TestList.should_return_200_with_custom_limit.approved.json +++ b/routers/notes/testdata/list_test.TestList.should_return_200_with_custom_limit.approved.json @@ -1,21 +1,24 @@ { - "nextCursor": "NQ==", + "authors": [ + { + "created": 1234567890, + "defaultWorkspaceId": 1, + "email": "karn@maek.ai", + "id": 1, + "name": "Karn", + "role": "admin", + "updated": 1234567890, + "verified": false + } + ], + "nextCursor": "MTA6NQ==", "notes": [ { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 9, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -28,37 +31,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 1, + "id": 9, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-1", + "updated": 18, + "updatedById": 1, + "uuid": "rand-uuid-9", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 8, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -71,37 +56,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 2, + "id": 8, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-2", + "updated": 16, + "updatedById": 1, + "uuid": "rand-uuid-8", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 7, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -114,37 +81,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 3, + "id": 7, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-3", + "updated": 14, + "updatedById": 1, + "uuid": "rand-uuid-7", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 6, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -157,37 +106,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 4, + "id": 6, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-4", + "updated": 12, + "updatedById": 1, + "uuid": "rand-uuid-6", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 5, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -202,17 +133,8 @@ "hasVideos": false, "id": 5, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, + "updated": 10, + "updatedById": 1, "uuid": "rand-uuid-5", "workspaceId": 1 } diff --git a/routers/notes/testdata/list_test.TestList.should_return_200_with_default_limit.approved.json b/routers/notes/testdata/list_test.TestList.should_return_200_with_default_limit.approved.json index 0e33410..bfd1d57 100644 --- a/routers/notes/testdata/list_test.TestList.should_return_200_with_default_limit.approved.json +++ b/routers/notes/testdata/list_test.TestList.should_return_200_with_default_limit.approved.json @@ -1,21 +1,24 @@ { - "nextCursor": "OQ==", + "authors": [ + { + "created": 1234567890, + "defaultWorkspaceId": 1, + "email": "karn@maek.ai", + "id": 1, + "name": "Karn", + "role": "admin", + "updated": 1234567890, + "verified": false + } + ], + "nextCursor": "", "notes": [ { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 9, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -28,37 +31,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 1, + "id": 9, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-1", + "updated": 18, + "updatedById": 1, + "uuid": "rand-uuid-9", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 8, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -71,37 +56,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 2, + "id": 8, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-2", + "updated": 16, + "updatedById": 1, + "uuid": "rand-uuid-8", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 7, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -114,37 +81,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 3, + "id": 7, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-3", + "updated": 14, + "updatedById": 1, + "uuid": "rand-uuid-7", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 6, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -157,37 +106,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 4, + "id": 6, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-4", + "updated": 12, + "updatedById": 1, + "uuid": "rand-uuid-6", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 5, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -202,35 +133,17 @@ "hasVideos": false, "id": 5, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, + "updated": 10, + "updatedById": 1, "uuid": "rand-uuid-5", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 4, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -243,37 +156,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 6, + "id": 4, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-6", + "updated": 8, + "updatedById": 1, + "uuid": "rand-uuid-4", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 3, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -286,37 +181,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 7, + "id": 3, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-7", + "updated": 6, + "updatedById": 1, + "uuid": "rand-uuid-3", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 2, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -329,37 +206,19 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 8, + "id": 2, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-8", + "updated": 4, + "updatedById": 1, + "uuid": "rand-uuid-2", "workspaceId": 1 }, { "content": { - "foo": "bar" - }, - "created": 1234567890, - "createdBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false + "dom": [] }, + "created": 1, + "createdById": 1, "favorite": true, "hasAudios": false, "hasClosedTasks": false, @@ -372,20 +231,11 @@ "hasQuotes": false, "hasTables": false, "hasVideos": false, - "id": 9, + "id": 1, "trashed": false, - "updated": 1234567890, - "updatedBy": { - "created": 1234567890, - "defaultWorkspaceId": 1, - "email": "karn@maek.ai", - "id": 1, - "name": "Karn", - "role": "admin", - "updated": 1234567890, - "verified": false - }, - "uuid": "rand-uuid-9", + "updated": 2, + "updatedById": 1, + "uuid": "rand-uuid-1", "workspaceId": 1 } ] diff --git a/routers/notes/upsert.go b/routers/notes/upsert.go index b96de5b..7363d17 100644 --- a/routers/notes/upsert.go +++ b/routers/notes/upsert.go @@ -52,12 +52,12 @@ func Upsert(ctx *base.WebContext) { note, err := notes.UpsertNote(rctx, ¬es.UpsertNoteRequest{ UUID: nuuid, - Content: string(contentBytes), + Content: contentBytes, Favorite: req.Favorite, Created: req.Created, Updated: req.Updated, WorkspaceID: ctx.Workspace.ID, - CreatedByID: ctx.Session.UserID, // only used if it's an insert, otherwise ignored + CreatedByID: ctx.Session.UserID, UpdatedByID: ctx.Session.UserID, HasContent: req.HasContent, HasImages: req.HasImages,