Files
summercms/cabana/schema_types.go
Jakub Zych aab4398ce4 feat(09-04): compile ordered Winter list schemas
- Typed columns, actions, default sort, search term, and page sizes
- Omitted sortable defaults to true and empty collections marshal as arrays
- Unknown keys, bad defaults, and path escape fail before routes are served
2026-09-24 18:46:53 +02:00

176 lines
5.7 KiB
Go

package cabana
import "encoding/json"
// ListSort is the compiled default sort. Direction is exactly asc or desc.
type ListSort struct {
Column string `json:"column"`
Direction string `json:"direction"`
}
// ListColumn is one compiled columns.yaml entry, in file order.
// Omitted sortable defaults to true, matching Winter; searchable defaults to false.
type ListColumn struct {
Key string `json:"key"`
Label string `json:"label"`
Searchable bool `json:"searchable"`
Sortable bool `json:"sortable"`
Type string `json:"type,omitempty"`
Relation string `json:"relation,omitempty"`
Select string `json:"select,omitempty"`
}
// ListFilter is one compiled config_filter scope. Values stay typed scalars.
type ListFilter struct {
Name string `json:"name"`
Label string `json:"label,omitempty"`
Type string `json:"type"`
Column string `json:"column,omitempty"`
Scope string `json:"scope,omitempty"`
ModelClass string `json:"modelClass,omitempty"`
NameFrom string `json:"nameFrom,omitempty"`
Options []FormOption `json:"options,omitempty"`
}
// RowAction is a per-record link derived from recordUrl.
type RowAction struct {
Name string `json:"name"`
Label string `json:"label,omitempty"`
URL string `json:"url,omitempty"`
}
// BulkAction is a checkbox action. It carries no SQL.
type BulkAction struct {
Name string `json:"name"`
Label string `json:"label,omitempty"`
}
// ListSchema is the boot-compiled list contract for one controller.
// Labels stay as source keys until a request localizes a copy.
type ListSchema struct {
Title string `json:"title,omitempty"`
ModelClass string `json:"modelClass,omitempty"`
RecordURL string `json:"recordUrl,omitempty"`
NoRecordsMessage string `json:"noRecordsMessage,omitempty"`
RecordsPerPage int `json:"recordsPerPage"`
PerPageOptions []int `json:"perPageOptions"`
ShowSearch bool `json:"showSearch"`
ShowSetup bool `json:"showSetup"`
ShowCheckboxes bool `json:"showCheckboxes"`
ShowSorting bool `json:"showSorting"`
SearchTerm string `json:"searchTerm"`
SearchPrompt string `json:"searchPrompt,omitempty"`
DefaultSort *ListSort `json:"defaultSort,omitempty"`
ToolbarButtons []string `json:"toolbarButtons"`
Columns []ListColumn `json:"columns"`
Filters []ListFilter `json:"filters"`
RowActions []RowAction `json:"rowActions"`
BulkActions []BulkAction `json:"bulkActions"`
}
// MarshalJSON keeps omitted collections as arrays so a partial copy cannot emit null.
func (s ListSchema) MarshalJSON() ([]byte, error) {
type alias ListSchema
out := alias(s)
if out.PerPageOptions == nil {
out.PerPageOptions = []int{}
}
if out.ToolbarButtons == nil {
out.ToolbarButtons = []string{}
}
if out.Columns == nil {
out.Columns = []ListColumn{}
}
if out.Filters == nil {
out.Filters = []ListFilter{}
}
if out.RowActions == nil {
out.RowActions = []RowAction{}
}
if out.BulkActions == nil {
out.BulkActions = []BulkAction{}
}
return json.Marshal(out)
}
// FormSchema is the locale-neutral form contract compiled once at boot.
// Display strings stay as source keys until a request localizes a copy.
type FormSchema struct {
Name string `json:"name,omitempty"`
ModelClass string `json:"modelClass,omitempty"`
Fields []FormField `json:"fields"`
}
// FormView is one request's localized form, including the locale actually used.
type FormView struct {
Name string `json:"name,omitempty"`
ModelClass string `json:"modelClass,omitempty"`
Fields []FormField `json:"fields"`
Meta FormMeta `json:"meta"`
}
// FormMeta reports the locale selected for a schema response.
type FormMeta struct {
Locale string `json:"locale"`
}
// FormField is one Winter field in source order. JSON keys keep Winter spelling.
type FormField struct {
Name string `json:"name"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
Comment string `json:"comment,omitempty"`
Span string `json:"span,omitempty"`
Tab string `json:"tab,omitempty"`
Size string `json:"size,omitempty"`
Context *fieldContext `json:"context,omitempty"`
NameFrom string `json:"nameFrom,omitempty"`
EmptyOption string `json:"emptyOption,omitempty"`
Relation string `json:"relation,omitempty"`
Required bool `json:"required,omitempty"`
Default *jsonScalar `json:"default,omitempty"`
Attributes map[string]jsonScalar `json:"attributes,omitempty"`
Options []FormOption `json:"options,omitempty"`
optionsMethod string
}
// FormOption is one dropdown choice. Value keeps the YAML scalar's JSON type.
type FormOption struct {
Value jsonScalar `json:"value"`
Label string `json:"label"`
}
// jsonScalar is a JSON scalar that still emits false, 0, and empty string.
type jsonScalar struct {
raw json.RawMessage
}
func (s jsonScalar) MarshalJSON() ([]byte, error) {
if len(s.raw) == 0 {
return []byte("null"), nil
}
return s.raw, nil
}
// fieldContext preserves a single Winter context string or a source-ordered list.
type fieldContext struct {
single bool
values []string
}
func (c fieldContext) MarshalJSON() ([]byte, error) {
if c.single {
text := ""
if len(c.values) > 0 {
text = c.values[0]
}
return json.Marshal(text)
}
values := c.values
if values == nil {
values = []string{}
}
return json.Marshal(values)
}