Skip to content

SEP-1825: Let an app declare its own settings class without editing core or migrations - #1360

Open
peter-o-addo wants to merge 11 commits into
mainfrom
SEP-1825
Open

SEP-1825: Let an app declare its own settings class without editing core or migrations#1360
peter-o-addo wants to merge 11 commits into
mainfrom
SEP-1825

Conversation

@peter-o-addo

@peter-o-addo peter-o-addo commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Let an app declare its own settings class without editing SettingClassEnum or adding CHECK-constraint migrations, by storing setting_class as a derived string and identifying classes by Pydantic __name__ on the API.

  • app/core/settings_override/models.py : add setting_class_token(), persist setting_class as a plain string, and keep enum-member name binding compatible
  • app/core/settings_override/{cache.py,api/routes.py,policy.py,registry.py,api/models.py,api/export.py,proxy.py,lifecycle.py} : switch settings-class handling from enum-centric to string/class-name-centric across runtime, API, and policy
  • app/sep/{api/routes/settings.py,apps/framework/registry.py,apps/alerts/{app_owned_settings.py,config.py},apps/report/{app_owned_settings.py,config.py},main.py} : wire app-owned classes by __name__ and keep callback/proxy wiring aligned
  • app/core/settings_override/{alembic_ops.py,constants.py}, app/core/db/utils.py, app/{sep,tasks,inventory}/migrations/env.py : add shared migration helpers and keep autogenerate clean for the string-backed setting_class
  • migration files : drop the settingoverride.setting_class CHECK on all tracks, remove obsolete health-report enum-extension revisions, and re-point child revisions
  • frontend/packages/api/specs/{sep,tasks,inventory}.json, frontend/packages/api/src/generated/{sep,tasks,inventory}.ts, frontend/packages/api/src/hooks/useSettings.ts : regenerate API types/specs and widen SettingClass to string
  • changelog.d/SEP-1825.changed.md : document the settings-class identifier change for operators
  • tests : add/adjust coverage for value-form paths, unregistered-class startup behavior, ALLOWED_KEYS parity, migration CHECK-drop behavior, and string-based class identifiers

Tested

  • GET a service-owned settings class by its class name succeeds.
  • GET that same class by its storage token is rejected.
  • GET an app-owned settings class by its class name succeeds.
  • GET that same class by its storage token is rejected.
  • PATCH a service-owned class by its class name applies the override.
  • PATCH an app-owned class by its class name applies the override.
  • Override rows in the database are stored under the storage token, not the class name.
  • DELETE a service-owned override by class name succeeds.
  • DELETE an app-owned override by class name succeeds.

Checklist

  • New/modified functions have type hints and rST docstrings
  • New tests added for new features or bug fixes
  • All tests pass locally (make test)
  • Pre-commit hooks pass (make run-pre-commit)
  • Database migrations generated if models changed (make makemigrations)
  • User-facing changes documented (README, inline help, UI text)
  • Configuration changes documented with examples
  • Changelog fragment added under changelog.d/ if the change is user-facing (make changelog-add), or confirmed N/A (internal-only change, or a same-release-cycle fix for an unreleased sibling ticket)

@github-actions github-actions Bot added python frontend app:alerts PR touches the alerts app slice app:report PR touches the report app slice svc:tasks PR touches the tasks service (app/tasks/) svc:inventory PR touches the inventory service (app/inventory/) large-diff Over 1500 changed lines, generated files discounted labels Aug 18, 2026
@peter-o-addo
peter-o-addo marked this pull request as ready for review August 18, 2026 08:56
@peter-o-addo
peter-o-addo requested a review from nachodd as a code owner August 18, 2026 08:56
Copilot AI lite review requested due to automatic review settings August 18, 2026 08:56
@peter-o-addo peter-o-addo added the qa in progress Someone is currently testing this PR - do not merge it label Aug 18, 2026
@peter-o-addo

Copy link
Copy Markdown
Contributor Author

1. GET SEPSettings200

curl -sS -i -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:8000/api/sep/admin/settings/SEPSettings/SYNC_REFRESH_TIME"
HTTP/1.1 200 OK
{"setting_class":"SEPSettings","key":"SYNC_REFRESH_TIME","value":5,"has_override":false}

2. GET SEP_SETTINGS404

curl -sS -i -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:8000/api/sep/admin/settings/SEP_SETTINGS/SYNC_REFRESH_TIME"
HTTP/1.1 404 Not Found
{"detail":"Settings class 'SEP_SETTINGS' is not exposed by this sub-app."}

3. GET AlertsSettings200

curl -sS -i -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:8000/api/sep/admin/settings/AlertsSettings/ALERT_FOLDER_NAME"
HTTP/1.1 200 OK
{"setting_class":"AlertsSettings","key":"ALERT_FOLDER_NAME","value":"SEP Alerts","has_override":false}

4. GET ALERTS_SETTINGS404

curl -sS -i -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:8000/api/sep/admin/settings/ALERTS_SETTINGS/ALERT_FOLDER_NAME"
HTTP/1.1 404 Not Found
{"detail":"Settings class 'ALERTS_SETTINGS' is not exposed by this sub-app."}

5. PATCH SEPSettings200

curl -sS -i -X PATCH "http://127.0.0.1:8000/api/sep/admin/settings/SEPSettings" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"SYNC_REFRESH_TIME":17}'
HTTP/1.1 200 OK
[{"setting_class":"SEPSettings","key":"SYNC_REFRESH_TIME","value":17,"has_override":true}]

6. PATCH AlertsSettings200

curl -sS -i -X PATCH "http://127.0.0.1:8000/api/sep/admin/settings/AlertsSettings" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"ALERT_FOLDER_NAME":"DB Token Check"}'
HTTP/1.1 200 OK
[{"setting_class":"AlertsSettings","key":"ALERT_FOLDER_NAME","value":"DB Token Check","has_override":true}]

7. DB stores storage tokens

sqlite3 sep.db "SELECT setting_class, key, value FROM settingoverride WHERE key IN ('SYNC_REFRESH_TIME','ALERT_FOLDER_NAME');"
SEP_SETTINGS|SYNC_REFRESH_TIME|17
ALERTS_SETTINGS|ALERT_FOLDER_NAME|"DB Token Check"

8. DELETE SEPSettings204

curl -sS -i -X DELETE \
  "http://127.0.0.1:8000/api/sep/admin/settings/SEPSettings/SYNC_REFRESH_TIME" \
  -H "Authorization: Bearer $TOKEN"
HTTP/1.1 204 No Content

9. DELETE AlertsSettings204

curl -sS -i -X DELETE \
  "http://127.0.0.1:8000/api/sep/admin/settings/AlertsSettings/ALERT_FOLDER_NAME" \
  -H "Authorization: Bearer $TOKEN"
HTTP/1.1 204 No Content

@peter-o-addo peter-o-addo added qa passed Tests for this PR are completed and successful. and removed qa in progress Someone is currently testing this PR - do not merge it labels Aug 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the tight coupling between “settings class identifiers” and a shared SettingClassEnum/DB CHECK constraint by shifting the public API to identify settings classes by Pydantic __name__ strings, while persisting override rows under a derived storage token (historically the enum member name). This enables SEP apps to declare app-owned settings classes without edits to core enums or per-track migrations.

Changes:

  • Replace enum-centric settings-class handling with string/class-name identifiers across settings override runtime, policy gates, and APIs.
  • Drop the settingoverride.setting_class CHECK constraint on SEP/Tasks/Inventory migration tracks (with shared idempotent Alembic helpers) and adjust tests accordingly.
  • Regenerate frontend OpenAPI specs/types to widen SettingClass to string and document the change via a changelog fragment.

Reviewed changes

Copilot reviewed 55 out of 58 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/sidecar/test_embedded_settings.py Updates allowlist reachability test to treat setting classes as strings.
tests/app/tasks/migrations/test_setting_class_enum.py Reworks migration tests to cover CHECK-drop behavior and downgrade cleanup.
tests/app/sep/test_settings_override_worker.py Updates worker override wiring tests for string-based app-owned class IDs.
tests/app/sep/test_settings_override_integration.py Updates integration expectations for app-owned class identifiers and storage tokens.
tests/app/sep/test_main.py Adjusts main proxy-map tests to use class-name identifiers for app-owned classes.
tests/app/sep/migrations/test_alembic_integration.py Adds SEP-track CHECK-drop/downgrade assertions.
tests/app/sep/apps/report/test_config.py Asserts report proxy uses class-name identifier, not enum member.
tests/app/sep/apps/framework/test_registry.py Updates app-owned settings registry tests for string identifiers.
tests/app/sep/apps/alerts/test_config.py Asserts alerts proxy uses class-name identifier and avoids collisions.
tests/app/sep/api/routes/test_settings.py Updates settings admin route tests for string-based class identifiers (404 on storage tokens).
tests/app/sep/api/routes/test_settings_proxy.py Updates proxy route ordering/contents assertions for string-based identifiers.
tests/app/sep/api/routes/test_settings_export.py Updates export tests to use class-name identifiers in exported blocks.
tests/app/migrations/test_shared_postgres_settingoverride.py Updates shared-DB migration tests for CHECK-drop and VARCHAR length behavior.
tests/app/core/settings_override/test_proxy.py Updates proxy tests to store class identifiers as strings.
tests/app/core/settings_override/test_policy.py Updates allowlist/policy tests to key by class-name strings and cover unregistered names.
tests/app/core/settings_override/test_policy_gates.py Adds coverage ensuring app-owned/unregistered classes honor allowlist by name.
tests/app/core/settings_override/test_models.py Adds tests pinning storage-token derivation (setting_class_token).
tests/app/core/settings_override/test_manager.py Asserts persisted override rows store the storage token (e.g. SEP_SETTINGS).
tests/app/core/settings_override/test_lifecycle.py Adds coverage for refresh behavior when override rows reference an unwired class.
tests/app/core/settings_override/api/test_app_owned_groups.py Updates API grouping/metadata tests for string-based setting class IDs.
tests/app/core/db/test_utils.py Adds compare-type tests for TypeDecorator(String(255)) equivalence.
frontend/packages/api/src/hooks/useSettings.ts Widens SettingClass typing to plain string and updates docs comment.
frontend/packages/api/src/generated/tasks.ts Regenerated Tasks client types: removes SettingClassEnum, uses string.
frontend/packages/api/src/generated/sep.ts Regenerated SEP client types: removes SettingClassEnum, uses string.
frontend/packages/api/src/generated/inventory.ts Regenerated Inventory client types: removes SettingClassEnum, uses string.
frontend/packages/api/specs/tasks.json Regenerated Tasks OpenAPI spec to remove SettingClassEnum and use string.
frontend/packages/api/specs/sep.json Regenerated SEP OpenAPI spec to remove SettingClassEnum and use string.
frontend/packages/api/specs/inventory.json Regenerated Inventory OpenAPI spec to remove SettingClassEnum and use string.
changelog.d/SEP-1825.changed.md Documents the settings-class identifier change for operators.
app/tasks/migrations/versions/2026_08_17_2210-7d2e869ac188_drop_setting_class_check_constraint.py Tasks-track migration to drop the CHECK using shared Alembic ops.
app/tasks/migrations/versions/2026_08_13_2143-a19da5cf0bca_add_taskhistory_log_state_capture_status.py Repoints Tasks migration chain to bypass a removed enum-extension revision.
app/tasks/migrations/versions/2026_08_12_1200-e2f3a4b5c6d7_extend_setting_class_enum_health_report.py Deleted obsolete Tasks enum-extension migration.
app/tasks/migrations/env.py Prevents Alembic logging config from disabling existing loggers.
app/sep/migrations/versions/2026_08_17_2210-74720aeda25b_drop_setting_class_check_constraint.py SEP-track migration to drop the CHECK using shared Alembic ops.
app/sep/migrations/versions/2026_08_12_1200-d1e2f3a4b5c6_extend_setting_class_enum_health_report.py Deleted obsolete SEP enum-extension migration.
app/sep/main.py Updates callback registry typing and wires app-owned class callback by class name.
app/sep/apps/report/config.py Keys report settings proxy by class __name__.
app/sep/apps/report/app_owned_settings.py Declares report app-owned settings using class __name__.
app/sep/apps/framework/registry.py Updates app-owned settings-class collection to dedupe by string identifier.
app/sep/apps/alerts/config.py Keys alerts settings proxy by class __name__.
app/sep/apps/alerts/app_owned_settings.py Declares alerts app-owned settings using class __name__.
app/sep/api/routes/settings.py Updates export/list logic to treat setting-class identifiers as strings.
app/inventory/migrations/versions/2026_08_17_2210-a38607bba456_drop_setting_class_check_constraint.py Inventory-track migration to drop the CHECK using shared Alembic ops.
app/inventory/migrations/versions/2026_08_12_1200-f3a4b5c6d7e8_extend_setting_class_enum_health_report.py Deleted obsolete Inventory enum-extension migration.
app/inventory/migrations/env.py Prevents Alembic logging config from disabling existing loggers.
app/core/settings_override/registry.py Switches policy gating to key off class-name strings rather than enum membership.
app/core/settings_override/proxy.py Updates proxy to accept setting_class: str.
app/core/settings_override/policy.py Updates allowlist functions to accept setting_class: str.
app/core/settings_override/models.py Introduces setting_class_token(), stores setting_class as string with enum-name compatibility.
app/core/settings_override/lifecycle.py Updates registry/callback typing and log messages for string identifiers.
app/core/settings_override/constants.py Adds the pre-SEP-1825 CHECK member list for downgrade restoration.
app/core/settings_override/cache.py Filters override rows by derived storage token (not enum value) and logs using class-name.
app/core/settings_override/api/routes.py Makes API path params strings and persists rows using derived storage tokens.
app/core/settings_override/api/models.py Updates response models to use setting_class: str.
app/core/settings_override/api/export.py Updates export helper to accept string setting-class identifiers.
app/core/settings_override/alembic_ops.py Adds shared, idempotent Alembic helpers to drop/restore the CHECK across tracks.
app/core/settings_override/init.py Re-exports setting_class_token and updates imports.
app/core/db/utils.py Adds check_constraint_name() and extends compare_type() to handle TypeDecorator(String).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread app/core/db/utils.py
Comment on lines +412 to +415
constraints = _check_constraints_for_column(bind, table_name, column_name)
if not constraints:
return None
return constraints[0].get("name")
Comment on lines 16 to 21
"""add taskhistory_log_state capture_status

Revision ID: a19da5cf0bca
Revises: e2f3a4b5c6d7
Revises: 6a19d56d7985
Create Date: 2026-08-13 21:43:58.711511

@github-actions

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  app/core/db
  utils.py 324, 333
  app/core/settings_override
  alembic_ops.py 55, 58, 60, 83, 85
  cache.py 202-207, 220-225
  lifecycle.py
  models.py 121
  policy.py
  proxy.py
  registry.py
  app/core/settings_override/api
  models.py
  routes.py 320, 742, 771, 815-818, 983-984, 1040, 1115, 1121
  app/sep
  inventory.py
  app/sep/api/routes
  settings.py
  app/sep/apps/framework
  registry.py
  app/sep/apps/report
  config.py
  app/sep/migrations/versions
  2026_06_02_1512-378c0872642f_extend_setting_class_enum_settings_alert.py 46
  2026_06_22_1400-c97e7e47c935_extend_setting_class_enum_anonymizer.py 46
  2026_06_30_1200-a1f4c7e9b2d3_extend_setting_class_enum_alerts.py 46
  2026_07_02_1001-a2b3c4d5e6f7_extend_setting_class_enum_inventory.py 58
  2026_08_17_2210-74720aeda25b_drop_setting_class_check_constraint.py
  app/sep/sync/syncers
  pmm.py
  app/tasks/migrations/versions
  2026_06_02_1512-6d4cfd37bd3a_extend_setting_class_enum_settings_alert.py 46
  2026_06_22_1400-abc65df0318a_extend_setting_class_enum_anonymizer.py 46
  2026_06_30_1200-b2e5d8f0c3a4_extend_setting_class_enum_alerts.py 46
  2026_07_02_1001-b3c4d5e6f7a8_extend_setting_class_enum_inventory.py 58
  2026_08_17_2210-7d2e869ac188_drop_setting_class_check_constraint.py
Project Total  

The report is truncated to 25 files out of 33. To see the full report, please visit the workflow summary page.

This report was generated by python-coverage-comment-action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app:alerts PR touches the alerts app slice app:report PR touches the report app slice frontend large-diff Over 1500 changed lines, generated files discounted python qa passed Tests for this PR are completed and successful. svc:inventory PR touches the inventory service (app/inventory/) svc:tasks PR touches the tasks service (app/tasks/)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants