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,omitempty"`
 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// GetNote retrieves a specific note by ID.
 87// Note: name and content fields are E2EE and will be null in the response.
 88func (c *Client) GetNote(ctx context.Context, noteID string) (*Note, error) {
 89	if noteID == "" {
 90		return nil, fmt.Errorf("%w: note ID cannot be empty", ErrBadRequest)
 91	}
 92
 93	resp, _, err := doJSON[noteResponse](c, ctx, http.MethodGet, "/notes/"+noteID, nil)
 94	if err != nil {
 95		return nil, err
 96	}
 97
 98	return &resp.Note, nil
 99}
100
101// CreateNote creates a new note in Lunatask.
102// Returns nil, nil if a matching note already exists in the same notebook
103// with the same source/source_id (HTTP 204).
104func (c *Client) CreateNote(ctx context.Context, note *CreateNoteRequest) (*Note, error) {
105	resp, noContent, err := doJSON[noteResponse](c, ctx, http.MethodPost, "/notes", note)
106	if err != nil {
107		return nil, err
108	}
109	if noContent {
110		return nil, nil
111	}
112
113	return &resp.Note, nil
114}
115
116// UpdateNote updates an existing note in Lunatask.
117func (c *Client) UpdateNote(ctx context.Context, noteID string, note *UpdateNoteRequest) (*Note, error) {
118	if noteID == "" {
119		return nil, fmt.Errorf("%w: note ID cannot be empty", ErrBadRequest)
120	}
121
122	resp, _, err := doJSON[noteResponse](c, ctx, http.MethodPut, "/notes/"+noteID, note)
123	if err != nil {
124		return nil, err
125	}
126
127	return &resp.Note, nil
128}
129
130// DeleteNote deletes a note in Lunatask.
131func (c *Client) DeleteNote(ctx context.Context, noteID string) (*Note, error) {
132	if noteID == "" {
133		return nil, fmt.Errorf("%w: note ID cannot be empty", ErrBadRequest)
134	}
135
136	resp, _, err := doJSON[noteResponse](c, ctx, http.MethodDelete, "/notes/"+noteID, nil)
137	if err != nil {
138		return nil, err
139	}
140
141	return &resp.Note, nil
142}