package sync import ( "context" "fmt" "net/http" "strings" "time" ) // demoConnector fabricates tickets locally (§11.11) so the entire // import→atomize→publish→claim flow is demonstrable offline. Deterministic: // the same project key always yields the same tickets. A project key ending // in "-ORPHAN" omits the last ticket from ListAssignedKeys to exercise the // orphan flow. type demoConnector struct { projectKey string } var demoTickets = []struct { suffix int typ string title string description string }{ {1, "epic", "Customer self-service portal", "Build a portal where customers can view invoices, update billing details, and download usage reports. Must integrate with the existing SSO and billing systems."}, {2, "story", "User import from CSV", "As an admin I can bulk-import users from a CSV file with column mapping, validation preview, and an idempotent re-run option. Errors are reported per row."}, {3, "story", "Email notification preferences", "As a user I can choose which events trigger email notifications (digest, immediate, off) and unsubscribe via a one-click link that does not require login."}, {4, "task", "Rate-limit public API endpoints", "Add per-token rate limiting to all public API endpoints with sensible defaults, 429 responses with Retry-After, and metrics for limit hits."}, {5, "task", "Fix timezone handling in reports", "Scheduled reports render dates in server time instead of the workspace timezone. Audit all report templates and normalize via the workspace setting."}, } func (d *demoConnector) Authorize(*http.Request) {} func (d *demoConnector) TestConnection(context.Context) error { return nil } func (d *demoConnector) key(i int) string { base := strings.TrimSuffix(strings.ToUpper(d.projectKey), "-ORPHAN") if base == "" { base = "DEMO" } return fmt.Sprintf("%s-%d", base, i) } func (d *demoConnector) FetchUpdated(_ context.Context, _ string, _ time.Time) ([]Ticket, error) { out := make([]Ticket, 0, len(demoTickets)) for _, dt := range demoTickets { out = append(out, Ticket{ Key: d.key(dt.suffix), URL: "https://demo.invalid/browse/" + d.key(dt.suffix), Type: dt.typ, Title: dt.title, Description: dt.description, Raw: map[string]any{"demo": true, "suffix": dt.suffix}, }) } return out, nil } func (d *demoConnector) ListAssignedKeys(context.Context, string) ([]string, error) { n := len(demoTickets) if strings.HasSuffix(strings.ToUpper(d.projectKey), "-ORPHAN") { n-- // last ticket "disappears" upstream → orphan flow } keys := make([]string, 0, n) for _, dt := range demoTickets[:n] { keys = append(keys, d.key(dt.suffix)) } return keys, nil }