feat(go): M5.1 — hand-author /api/* wire types + JSON Schemas
All checks were successful
Deploy to K8s / deploy (push) Successful in 7s
All checks were successful
Deploy to K8s / deploy (push) Successful in 7s
Add internal/web/api package with Go structs for every /api/X route:
AdultsResponse, JuniorsResponse, PaymentsResponse, VersionResponse.
All fields carry explicit json: tags matching the Python view-model keys.
Key design choices:
- member_data / month_labels / raw_payments are nested objects (not
the pre-serialised JSON strings used in Jinja templates)
- Expected{Value int; Unknown bool} with custom MarshalJSON emits int
or the string "?" for junior single-attendance months
- RawTransaction covers the full 11-column payments sheet row
schemagen_test.go reflects all four response types via
github.com/invopop/jsonschema and golden-compares against committed
schemas in tests/fixtures/api-schema/. The JSONSchema() method on
Expected lives in the test file so the prod binary has no jsonschema
dependency.
Closes M5.1 in docs/plans/2026-05-03-2349-go-backend-rewrite-progress.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
42
go/internal/web/api/adults.go
Normal file
42
go/internal/web/api/adults.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package api
|
||||
|
||||
// AdultsMonthData is the reconciled ledger for one adult member in one month.
|
||||
// Keys match Python's result["members"][name]["months"][YYYY-MM].
|
||||
type AdultsMonthData struct {
|
||||
Expected int `json:"expected"`
|
||||
OriginalExpected int `json:"original_expected"`
|
||||
AttendanceCount int `json:"attendance_count"`
|
||||
Exception *ExceptionData `json:"exception"`
|
||||
Paid float64 `json:"paid"` // float: proportional allocator may produce fractional CZK
|
||||
Transactions []MemberTxEntry `json:"transactions"`
|
||||
}
|
||||
|
||||
// AdultsMemberData is the reconciled ledger for one adult member.
|
||||
// Keys match Python's result["members"][name].
|
||||
type AdultsMemberData struct {
|
||||
Tier string `json:"tier"`
|
||||
Months map[string]AdultsMonthData `json:"months"` // YYYY-MM → month data
|
||||
OtherTransactions []MemberOtherEntry `json:"other_transactions"`
|
||||
TotalBalance int `json:"total_balance"`
|
||||
}
|
||||
|
||||
// AdultsResponse is the JSON contract for GET /api/adults.
|
||||
// MemberData, MonthLabels, and RawPayments correspond to the Python view-model
|
||||
// fields member_data, month_labels_json, and raw_payments_json respectively,
|
||||
// but as nested objects rather than pre-serialised JSON strings.
|
||||
type AdultsResponse struct {
|
||||
Months []string `json:"months"` // display labels
|
||||
RawMonths []string `json:"raw_months"` // "YYYY-MM"
|
||||
Results []MemberRow `json:"results"`
|
||||
Totals []TotalCell `json:"totals"`
|
||||
MemberData map[string]AdultsMemberData `json:"member_data"` // name → ledger
|
||||
MonthLabels map[string]string `json:"month_labels"` // YYYY-MM → display label
|
||||
RawPayments map[string][]RawTransaction `json:"raw_payments"` // name → raw sheet rows
|
||||
Credits []Credit `json:"credits"`
|
||||
Debts []Credit `json:"debts"`
|
||||
Unmatched []RawTransaction `json:"unmatched"`
|
||||
AttendanceURL string `json:"attendance_url"`
|
||||
PaymentsURL string `json:"payments_url"`
|
||||
BankAccount string `json:"bank_account"`
|
||||
CurrentMonth string `json:"current_month"`
|
||||
}
|
||||
Reference in New Issue
Block a user