Files
honeyDueAPI/internal/dto/requests/task.go
Trey t bb7493f033 Close all 25 codex audit findings and add KMP contract tests
Remediate all P0-S priority findings from cross-platform architecture audit:
- Add input validation and authorization checks across handlers
- Harden social auth (Apple/Google) token validation
- Add document ownership verification and file type validation
- Add rate limiting config and CORS origin restrictions
- Add subscription tier enforcement in handlers
- Add OpenAPI 3.0.3 spec (81 schemas, 104 operations)
- Add URL-level contract test (KMP API routes match spec paths)
- Add model-level contract test (65 schemas, 464 fields validated)
- Add CI workflow for backend tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:15:07 -06:00

110 lines
3.7 KiB
Go

package requests
import (
"encoding/json"
"strings"
"time"
"github.com/shopspring/decimal"
)
// FlexibleDate handles both "2025-11-27" and "2025-11-27T00:00:00Z" formats
type FlexibleDate struct {
time.Time
}
func (fd *FlexibleDate) UnmarshalJSON(data []byte) error {
// Remove quotes
s := strings.Trim(string(data), "\"")
if s == "" || s == "null" {
return nil
}
// Try RFC3339 first (full datetime)
t, err := time.Parse(time.RFC3339, s)
if err == nil {
fd.Time = t
return nil
}
// Try date-only format
t, err = time.Parse("2006-01-02", s)
if err == nil {
fd.Time = t
return nil
}
return err
}
func (fd FlexibleDate) MarshalJSON() ([]byte, error) {
if fd.Time.IsZero() {
return json.Marshal(nil)
}
return json.Marshal(fd.Time.Format(time.RFC3339))
}
// ToTimePtr returns a pointer to the underlying time, or nil if zero
func (fd *FlexibleDate) ToTimePtr() *time.Time {
if fd == nil || fd.Time.IsZero() {
return nil
}
return &fd.Time
}
// CreateTaskRequest represents the request to create a task
type CreateTaskRequest struct {
ResidenceID uint `json:"residence_id" validate:"required"`
Title string `json:"title" validate:"required,min=1,max=200"`
Description string `json:"description"`
CategoryID *uint `json:"category_id"`
PriorityID *uint `json:"priority_id"`
FrequencyID *uint `json:"frequency_id"`
CustomIntervalDays *int `json:"custom_interval_days"` // For "Custom" frequency, user-specified days
InProgress bool `json:"in_progress"`
AssignedToID *uint `json:"assigned_to_id"`
DueDate *FlexibleDate `json:"due_date"`
EstimatedCost *decimal.Decimal `json:"estimated_cost"`
ContractorID *uint `json:"contractor_id"`
}
// UpdateTaskRequest represents the request to update a task
type UpdateTaskRequest struct {
Title *string `json:"title" validate:"omitempty,min=1,max=200"`
Description *string `json:"description"`
CategoryID *uint `json:"category_id"`
PriorityID *uint `json:"priority_id"`
FrequencyID *uint `json:"frequency_id"`
CustomIntervalDays *int `json:"custom_interval_days"` // For "Custom" frequency, user-specified days
InProgress *bool `json:"in_progress"`
AssignedToID *uint `json:"assigned_to_id"`
DueDate *FlexibleDate `json:"due_date"`
EstimatedCost *decimal.Decimal `json:"estimated_cost"`
ActualCost *decimal.Decimal `json:"actual_cost"`
ContractorID *uint `json:"contractor_id"`
}
// CreateTaskCompletionRequest represents the request to create a task completion
type CreateTaskCompletionRequest struct {
TaskID uint `json:"task_id" validate:"required"`
CompletedAt *time.Time `json:"completed_at"` // Defaults to now
Notes string `json:"notes"`
ActualCost *decimal.Decimal `json:"actual_cost"`
Rating *int `json:"rating"` // 1-5 star rating
ImageURLs []string `json:"image_urls"` // Multiple image URLs
}
// UpdateTaskCompletionRequest represents the request to update a task completion
type UpdateTaskCompletionRequest struct {
Notes *string `json:"notes"`
ActualCost *decimal.Decimal `json:"actual_cost"`
Rating *int `json:"rating"`
ImageURLs []string `json:"image_urls"`
}
// CompletionImageInput represents an image to add to a completion
type CompletionImageInput struct {
ImageURL string `json:"image_url" validate:"required"`
Caption string `json:"caption"`
}