fix(soccer-scoreboard): let custom leagues (eng.2 etc.) save - #238
Conversation
Adding any league under "Add More Leagues" failed to save with HTTP 400, regardless of the league code entered — so the English Championship (eng.2) could not be configured at all. The web UI's array-table row editor stores every non-column property of a row in a hidden text input: an empty input comes back as null, and a list property comes back as the comma-separated string the user typed. custom_leagues declared those properties as strict "array"/"object"/"number", so a brand-new row submitted favorite_teams=null, exclude_teams=null, dynamic_duration.max_duration_seconds=null and dynamic_duration.modes=null, and the core's Draft-7 validation rejected the whole config. - Schema: the properties the row editor can leave empty now accept null, and the team lists also accept the comma-separated text the editor produces. - Manager: _normalize_custom_leagues() strips those nulls (so every downstream .get(key, default) falls back to its default), splits team strings back into lists, and trims/lowercases the league code. It runs from _load_custom_leagues(), i.e. on both startup and runtime config change. Previously a null "modes" also crashed supports_dynamic_duration(). - league_code pattern no longer rejects valid ESPN codes with underscores or prefixes longer than four letters (eng.league_cup, conmebol.libertadores). test_custom_league_config.py rebuilds the exact payload array-table.js submits for a new row and asserts it validates and normalizes correctly (32 checks). Also verified end-to-end with the core safety harness: feeding the raw web-UI payload as a config override registers and renders soccer_eng.2_live/recent/ upcoming. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FNF7jfQcu6dF63weiNZH93
|
Warning Review limit reached
Next review available in: 36 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 48 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codacy flagged one new critical ErrorProne issue: pylint reads `SoccerScoreboardPlugin._adapt_config_for_custom_league.__get__(plugin)(...)` as an unbound call missing two arguments (E1120). The manual descriptor binding was pointless — the test's plugin object is a real instance, so it can call the method directly. Also swaps the unused `import jsonschema` availability probe for importlib.util.find_spec and renames two locals that shadowed module-level names. No behavior change; still 32/32 checks passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FNF7jfQcu6dF63weiNZH93
Pull Request
Summary
Adding a league under Add More Leagues failed to save with HTTP 400 no matter which league code was entered, so the English Championship (
eng.2) could not be configured at all. Thecustom_leaguesschema now accepts the values the web UI's row editor actually submits, and the manager normalizes them back to the shapes it expects.Type of change
Plugin(s) affected
soccer-scoreboardRelated issues
N/A — reported directly.
Root cause
The array-table row editor keeps every non-column property of a row in a hidden text input. An empty input round-trips back through
coerceValue()asnull, and a list property comes back as the comma-separated string the user typed.custom_leaguesdeclared those properties as strict"array"/"object"/"number", so a brand-new row submitted:Draft-7 validation rejected all four and blocked the entire config save — the league code was never the problem.
Changes
config_schema.json— properties the row editor can leave empty now acceptnull;favorite_teams/exclude_teamsalso accept the comma-separated text the editor produces.config_schema.json— theleague_codepattern no longer rejects valid ESPN codes with underscores or prefixes longer than four letters (eng.league_cup,conmebol.libertadores); the field description and the "Add More Leagues" hint now listeng.2and friends.manager.py— new_normalize_custom_leagues()strips the nulls (so every downstream.get(key, default)falls back to its default), splits team strings into lists, and trims/lowercases the league code. It runs from_load_custom_leagues(), i.e. on startup and on runtime config change. A nullmodespreviously also crashedsupports_dynamic_duration().README.md— documents adding a league, that a row needs both a name and a code, and a table of common ESPN codes.Test plan
(
EMULATOR=true python3 run.py)(
scripts/dev_server.py)Details:
test_custom_league_config.pyrebuilds the exact payloadarray-table.jssubmits for a new row (sameString()/coerceValue()round-trip) and asserts it validates against the schema and normalizes correctly — 32 checks, all passing. It fails on the old schema with the four errors above.--configoverride —soccer_eng.2_live/recent/upcomingregister and render.test_live_mode_targeting,test_live_screens,test_goal_celebration,test_non_favorite_live_duration,test_timezone_resolution,test_world_cup_flags) still pass.python scripts/check_module_collisions.py→ OK across 42 plugins.Required for plugin changes
versioninplugins/<id>/manifest.json(2.4.0 → 2.4.1)class_nameinmanifest.jsonmatches the actual class inmanager.pyexactlyentry_pointmatches the real file (or is omitted to use themanager.pydefault)README.mdif config keys changedconfig_schema.jsonis the source of truth for the web UI form — any new option is in the schema with adefault,description, and constraintsplugins.json)Checklist
CONTRIBUTING.mdCONTRIBUTING.mdandCODE_OF_CONDUCT.mdNotes for reviewer
Fixed in the plugin rather than the core so existing installs get it through a plugin-store update. The same widget behaviour affects any plugin using
x-widget: array-tablewith array/object properties outsidex-columns;countdownandstock-newswere checked and are not affected today.The Championship stays a custom league rather than becoming a predefined one — that keeps the change to the bug, and the custom-league path is the designed mechanism. Happy to promote
eng.2intoleaguesif you'd rather it ship with its own settings block.Generated by Claude Code