people.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// Person represents a person/relationship returned from the Lunatask API.
 16// Note: first_name and last_name are E2EE and not returned by the API.
 17type Person struct {
 18	ID                   string    `json:"id"`
 19	RelationshipStrength *string   `json:"relationship_strength"`
 20	Sources              []Source  `json:"sources"`
 21	CreatedAt            time.Time `json:"created_at"`
 22	UpdatedAt            time.Time `json:"updated_at"`
 23}
 24
 25// CreatePersonRequest represents the request to create a person in Lunatask.
 26type CreatePersonRequest struct {
 27	FirstName            *string `json:"first_name,omitempty"`
 28	LastName             *string `json:"last_name,omitempty"`
 29	RelationshipStrength *string `json:"relationship_strength,omitempty"`
 30	Source               *string `json:"source,omitempty"`
 31	SourceID             *string `json:"source_id,omitempty"`
 32}
 33
 34// personResponse represents a single person response from the API.
 35type personResponse struct {
 36	Person Person `json:"person"`
 37}
 38
 39// peopleResponse represents a list of people response from the API.
 40type peopleResponse struct {
 41	People []Person `json:"people"`
 42}
 43
 44// ListPeopleOptions contains optional filters for listing people.
 45type ListPeopleOptions struct {
 46	Source   *string
 47	SourceID *string
 48}
 49
 50// ListPeople retrieves all people, optionally filtered by source and/or source_id.
 51func (c *Client) ListPeople(ctx context.Context, opts *ListPeopleOptions) ([]Person, error) {
 52	path := "/people"
 53
 54	if opts != nil {
 55		params := url.Values{}
 56		if opts.Source != nil && *opts.Source != "" {
 57			params.Set("source", *opts.Source)
 58		}
 59		if opts.SourceID != nil && *opts.SourceID != "" {
 60			params.Set("source_id", *opts.SourceID)
 61		}
 62		if len(params) > 0 {
 63			path = fmt.Sprintf("%s?%s", path, params.Encode())
 64		}
 65	}
 66
 67	resp, _, err := doJSON[peopleResponse](c, ctx, http.MethodGet, path, nil)
 68	if err != nil {
 69		return nil, err
 70	}
 71
 72	return resp.People, nil
 73}
 74
 75// GetPerson retrieves a specific person by ID.
 76func (c *Client) GetPerson(ctx context.Context, personID string) (*Person, error) {
 77	if personID == "" {
 78		return nil, fmt.Errorf("%w: person ID cannot be empty", ErrBadRequest)
 79	}
 80
 81	resp, _, err := doJSON[personResponse](c, ctx, http.MethodGet, "/people/"+personID, nil)
 82	if err != nil {
 83		return nil, err
 84	}
 85
 86	return &resp.Person, nil
 87}
 88
 89// CreatePerson creates a new person/relationship in Lunatask.
 90// Returns nil, nil if a matching person already exists with the same
 91// source/source_id (HTTP 204).
 92func (c *Client) CreatePerson(ctx context.Context, person *CreatePersonRequest) (*Person, error) {
 93	resp, noContent, err := doJSON[personResponse](c, ctx, http.MethodPost, "/people", person)
 94	if err != nil {
 95		return nil, err
 96	}
 97	if noContent {
 98		return nil, nil
 99	}
100
101	return &resp.Person, nil
102}
103
104// DeletePerson deletes a person in Lunatask.
105func (c *Client) DeletePerson(ctx context.Context, personID string) (*Person, error) {
106	if personID == "" {
107		return nil, fmt.Errorf("%w: person ID cannot be empty", ErrBadRequest)
108	}
109
110	resp, _, err := doJSON[personResponse](c, ctx, http.MethodDelete, "/people/"+personID, nil)
111	if err != nil {
112		return nil, err
113	}
114
115	return &resp.Person, nil
116}