@@ -329,8 +329,9 @@ func (b *PersonBuilder) WithLastName(name string) *PersonBuilder {
return b
}
-// WithRelationshipStrength categorizes how close you are: close, regular, distant, or acquaintance.
-func (b *PersonBuilder) WithRelationshipStrength(strength string) *PersonBuilder {
+// WithRelationshipStrength categorizes the closeness of the relationship.
+// Use one of the Relationship* constants (e.g., RelationshipCloseFriend).
+func (b *PersonBuilder) WithRelationshipStrength(strength RelationshipStrength) *PersonBuilder {
b.req.RelationshipStrength = &strength
return b
@@ -345,6 +346,19 @@ func (b *PersonBuilder) FromSource(source, sourceID string) *PersonBuilder {
return b
}
+// WithCustomField sets an arbitrary custom field. Lunatask supports "email",
+// "birthday", and "phone" out of the box; other fields must first be defined
+// in the app or [ErrUnprocessableEntity] is returned.
+func (b *PersonBuilder) WithCustomField(key string, value any) *PersonBuilder {
+ if b.req.CustomFields == nil {
+ b.req.CustomFields = make(map[string]any)
+ }
+
+ b.req.CustomFields[key] = value
+
+ return b
+}
+
// Build returns the constructed request.
func (b *PersonBuilder) Build() *CreatePersonRequest {
return &b.req
@@ -6,7 +6,9 @@ package lunatask
import (
"context"
+ "encoding/json"
"fmt"
+ "maps"
"net/http"
"net/url"
"time"
@@ -15,21 +17,57 @@ import (
// Person is a contact in Lunatask's relationship tracker.
// FirstName and LastName are encrypted client-side and will be null when read.
type Person struct {
- ID string `json:"id"`
- RelationshipStrength *string `json:"relationship_strength"`
- Sources []Source `json:"sources"`
- CreatedAt time.Time `json:"created_at"`
- UpdatedAt time.Time `json:"updated_at"`
+ ID string `json:"id"`
+ RelationshipStrength *RelationshipStrength `json:"relationship_strength"`
+ Sources []Source `json:"sources"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
}
// CreatePersonRequest defines a new person.
// Use [PersonBuilder] for a fluent construction API.
+//
+// CustomFields allows setting arbitrary custom fields. Lunatask supports
+// "email", "birthday", and "phone" out of the box; other fields must first be
+// defined in the app or [ErrUnprocessableEntity] is returned. These are
+// flattened to top-level JSON fields when marshaled.
type CreatePersonRequest struct {
- FirstName *string `json:"first_name,omitempty"`
- LastName *string `json:"last_name,omitempty"`
- RelationshipStrength *string `json:"relationship_strength,omitempty"`
- Source *string `json:"source,omitempty"`
- SourceID *string `json:"source_id,omitempty"`
+ FirstName *string `json:"first_name,omitempty"`
+ LastName *string `json:"last_name,omitempty"`
+ RelationshipStrength *RelationshipStrength `json:"relationship_strength,omitempty"`
+ Source *string `json:"source,omitempty"`
+ SourceID *string `json:"source_id,omitempty"`
+ CustomFields map[string]any `json:"-"`
+}
+
+// MarshalJSON flattens CustomFields to top-level JSON fields.
+func (r CreatePersonRequest) MarshalJSON() ([]byte, error) {
+ // Alias to avoid infinite recursion
+ type plain CreatePersonRequest
+
+ base, err := json.Marshal(plain(r))
+ if err != nil {
+ return nil, fmt.Errorf("marshaling person request: %w", err)
+ }
+
+ if len(r.CustomFields) == 0 {
+ return base, nil
+ }
+
+ // Merge custom fields into the base object
+ var merged map[string]any
+ if err := json.Unmarshal(base, &merged); err != nil {
+ return nil, fmt.Errorf("unmarshaling person request for merge: %w", err)
+ }
+
+ maps.Copy(merged, r.CustomFields)
+
+ data, err := json.Marshal(merged)
+ if err != nil {
+ return nil, fmt.Errorf("marshaling merged person request: %w", err)
+ }
+
+ return data, nil
}
// personResponse wraps a single person from the API.
@@ -87,3 +87,17 @@ func ParseDate(s string) (Date, error) {
func Today() Date {
return NewDate(time.Now())
}
+
+// RelationshipStrength categorizes the closeness of a relationship.
+type RelationshipStrength string
+
+// Valid relationship strength values.
+const (
+ RelationshipFamily RelationshipStrength = "family"
+ RelationshipIntimateFriend RelationshipStrength = "intimate-friends"
+ RelationshipCloseFriend RelationshipStrength = "close-friends"
+ RelationshipCasualFriend RelationshipStrength = "casual-friends"
+ RelationshipAcquaintance RelationshipStrength = "acquaintances"
+ RelationshipBusiness RelationshipStrength = "business-contacts"
+ RelationshipAlmostStranger RelationshipStrength = "almost-strangers"
+)