notes.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package lunatask
  6
  7import (
  8	"context"
  9	"fmt"
 10	"net/http"
 11	"net/url"
 12	"time"
 13)
 14
 15// Note represents a note returned from the Lunatask API.
 16// Note: name and content are E2EE and not returned by the API.
 17type Note struct {
 18	ID         string    `json:"id"`
 19	NotebookID string    `json:"notebook_id"`
 20	DateOn     *Date     `json:"date_on"`
 21	Sources    []Source  `json:"sources"`
 22	CreatedAt  time.Time `json:"created_at"`
 23	UpdatedAt  time.Time `json:"updated_at"`
 24}
 25
 26// CreateNoteRequest represents the request to create a note in Lunatask.
 27type CreateNoteRequest struct {
 28	Name       *string `json:"name,omitempty"`
 29	Content    *string `json:"content,omitempty"`
 30	NotebookID string  `json:"notebook_id"`
 31	Source     *string `json:"source,omitempty"`
 32	SourceID   *string `json:"source_id,omitempty"`
 33}
 34
 35// UpdateNoteRequest represents the request to update a note in Lunatask.
 36// All fields are optional; only provided fields will be updated.
 37// Note: updating content replaces the entire content (E2EE prevents appending).
 38type UpdateNoteRequest struct {
 39	Name       *string `json:"name,omitempty"`
 40	Content    *string `json:"content,omitempty"`
 41	NotebookID *string `json:"notebook_id,omitempty"`
 42	DateOn     *Date   `json:"date_on,omitempty"`
 43}
 44
 45// noteResponse represents a single note response from the API.
 46type noteResponse struct {
 47	Note Note `json:"note"`
 48}
 49
 50// notesResponse represents a list of notes response from the API.
 51type notesResponse struct {
 52	Notes []Note `json:"notes"`
 53}
 54
 55// ListNotesOptions contains optional filters for listing notes.
 56type ListNotesOptions struct {
 57	Source   *string
 58	SourceID *string
 59}
 60
 61// ListNotes retrieves all notes, optionally filtered by source and/or source_id.
 62func (c *Client) ListNotes(ctx context.Context, opts *ListNotesOptions) ([]Note, error) {
 63	path := "/notes"
 64
 65	if opts != nil {
 66		params := url.Values{}
 67		if opts.Source != nil && *opts.Source != "" {
 68			params.Set("source", *opts.Source)
 69		}
 70		if opts.SourceID != nil && *opts.SourceID != "" {
 71			params.Set("source_id", *opts.SourceID)
 72		}
 73		if len(params) > 0 {
 74			path = fmt.Sprintf("%s?%s", path, params.Encode())
 75		}
 76	}
 77
 78	resp, _, err := doJSON[notesResponse](c, ctx, http.MethodGet, path, nil)
 79	if err != nil {
 80		return nil, err
 81	}
 82
 83	return resp.Notes, nil
 84}
 85
 86// CreateNote creates a new note in Lunatask.
 87// Returns nil, nil if a matching note already exists in the same notebook
 88// with the same source/source_id (HTTP 204).
 89func (c *Client) CreateNote(ctx context.Context, note *CreateNoteRequest) (*Note, error) {
 90	if note.NotebookID == "" {
 91		return nil, fmt.Errorf("%w: notebook_id is required", ErrBadRequest)
 92	}
 93
 94	resp, noContent, err := doJSON[noteResponse](c, ctx, http.MethodPost, "/notes", note)
 95	if err != nil {
 96		return nil, err
 97	}
 98	if noContent {
 99		return nil, nil
100	}
101
102	return &resp.Note, nil
103}
104
105// UpdateNote updates an existing note in Lunatask.
106func (c *Client) UpdateNote(ctx context.Context, noteID string, note *UpdateNoteRequest) (*Note, error) {
107	if noteID == "" {
108		return nil, fmt.Errorf("%w: note ID cannot be empty", ErrBadRequest)
109	}
110
111	resp, _, err := doJSON[noteResponse](c, ctx, http.MethodPut, "/notes/"+noteID, note)
112	if err != nil {
113		return nil, err
114	}
115
116	return &resp.Note, nil
117}
118
119// DeleteNote deletes a note in Lunatask.
120func (c *Client) DeleteNote(ctx context.Context, noteID string) (*Note, error) {
121	if noteID == "" {
122		return nil, fmt.Errorf("%w: note ID cannot be empty", ErrBadRequest)
123	}
124
125	resp, _, err := doJSON[noteResponse](c, ctx, http.MethodDelete, "/notes/"+noteID, nil)
126	if err != nil {
127		return nil, err
128	}
129
130	return &resp.Note, nil
131}