Commit 545c4e2
Migrate queries to SQLAlchemy 2.0 select() API (#755)
* refactor(queries): Migrate app_server.py to SQLAlchemy 2.0 select() API
- Replace session.query(User).filter() with select().where()
- Replace session.query(Model).get(id) with session.get(Model, id)
- Replace session.query(Session).delete() with session.execute(delete())
- Remove test_initialization_detects_admin_exists (duplicated coverage)
- Add test_configure_jwt_creates_secret_if_missing for JWT initialization
- All tests passing, no deprecation warnings
Migrated 7 query patterns (lines 110, 119, 131, 133, 150, 307-309, 349)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(queries): Migrate models/script.py to SQLAlchemy 2.0 select() API
- Replace session.query().filter().all() with select().where()
- Replace with_entities() subqueries with select() for columns
- Replace func aggregation queries with session.execute().scalar()
- Replace composite key .get(dict) with session.get(Model, tuple)
- Add test coverage for StageDirectionStyle.pre_delete() hook
- Add test coverage for CompiledScript.compile_script() method
- Add test coverage for CompiledScript.load_compiled_script() method
- All tests passing, no deprecation warnings
Migrated 5 query patterns (lines 204-210, 242-249, 258-263, 285-288)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* style: Fix formatting in test_digi_server.py
- Remove extra blank line per ruff formatting rules
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(queries): Migrate rbac/rbac_db.py to SQLAlchemy 2.0 select() API
- Replace session.query().filter_by() with select().filter_by()
- Add test coverage for delete_actor method in RBAC system
- Test verifies RBAC assignments are properly deleted
- All tests passing (12 total), no deprecation warnings
- Code formatting and linting checks passing
Migrated 1 query pattern (line 247) in _delete_from_rbac_db method
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(migration): Migrate script controller queries to SQLAlchemy 2.0
Migrated 14 legacy query patterns in controllers/api/show/script/script.py:
- 7× session.query(Script).filter().first() → session.scalars(select().where()).first()
- 3× session.query(ScriptCuts).filter().all() → session.scalars(select().where()).all()
- 2× session.query(ScriptLineRevisionAssociation).filter().all() with .has() → session.scalars(select().where()).all()
- 2× session.query().with_entities() subqueries → select() with session.execute().scalar()
- 11× session.query().get(dict) → session.get(Model, tuple) for composite keys
Created comprehensive test coverage (9 tests) in test/test_script_controller.py:
- Tests for all 4 controllers (Script, CompiledScript, ScriptCuts, ScriptMaxPage)
- Covers GET endpoints via HTTP requests
- Verifies query patterns directly for complex composite key operations
- All tests passing ✅
Changes maintain backward compatibility and pass all existing tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(migration): Migrate script revisions controller to SQLAlchemy 2.0
Migrated 5 legacy query patterns in controllers/api/show/script/revisions.py:
- 5× session.query(Script).filter().first() → session.scalars(select().where()).first()
All patterns were the same simple filter_first pattern across two controllers:
- ScriptRevisionsController (GET, POST, DELETE)
- ScriptCurrentRevisionController (GET, POST)
Created comprehensive test coverage (4 tests) in test/test_script_revisions_controller.py:
- Tests for GET /api/v1/show/script/revisions
- Tests for GET /api/v1/show/script/revisions/current
- Verifies proper 404 handling when script not found
- All tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(migration): Migrate stage direction styles controller to SQLAlchemy 2.0
Migrated 4 legacy query patterns in controllers/api/show/script/stage_direction_styles.py:
- 4× session.query(Script).filter().first() → session.scalars(select().where()).first()
All patterns were the same simple filter_first pattern across StageDirectionStylesController:
- GET: Retrieve all stage direction styles for a script
- POST: Create new stage direction style
- PATCH: Update existing stage direction style
- DELETE: Remove stage direction style
Created comprehensive test coverage (2 tests) in test/test_stage_direction_styles_controller.py:
- Tests for GET /api/v1/show/script/stage_direction_styles
- Verifies correct retrieval and empty list handling
- All tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(server): Migrate config.py to SQLAlchemy 2.0 syntax
Migrate controllers/api/show/script/config.py to use SQLAlchemy 2.0
select() API. Add comprehensive test coverage.
Changes:
- Migrate 1 filter_all pattern from query() to select() API
- Add test_script_config_controller.py with 2 tests
- All tests passing
Pattern migrated:
- session.query(Session).filter().all() →
session.scalars(select(Session).where()).all()
This completes Priority 3: Script Sub-controllers (10 patterns total).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(test): Reorganize test directory to mirror application structure
Restructure test directory to follow the same hierarchy as the
application controllers and models, improving organization and
maintainability.
Changes:
- Create test/api/, test/api/show/script/, and test/models/ directories
- Move controller tests to test/api/show/script/:
* test_config.py (was test_script_config_controller.py)
* test_revisions.py (was test_script_revisions_controller.py)
* test_script.py (was test_script_controller.py)
* test_stage_direction_styles.py (was test_stage_direction_styles_controller.py)
- Move API tests to test/api/:
* test_auth.py (was test_auth_api.py)
* test_rbac.py
* test_settings.py
- Move model tests to test/models/:
* test_script.py (was test_script_models.py)
- Update all imports from relative to absolute (test.test_utils)
- Add __init__.py files to all new directories
All 57 tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(server): Migrate show controllers to SQLAlchemy 2.0 syntax
Migrate all show-related controllers (shows, acts, scenes, characters,
cast, and cues) to use SQLAlchemy 2.0 select() API.
Changes:
- Migrate 11 query patterns across 6 files
- shows.py: 1 pattern (simple all())
- acts.py: 1 pattern (filter_all)
- scenes.py: 1 pattern (filter_all)
- characters.py: 1 pattern (filter_first)
- cast.py: 1 pattern (filter_first)
- cues.py: 6 patterns (5x filter_first Script, 1x filter_all CueAssociation)
Pattern migrations:
- session.query(Model).all() → session.scalars(select(Model)).all()
- session.query(Model).filter().all() → session.scalars(select(Model).where()).all()
- session.query(Model).filter().first() → session.scalars(select(Model).where()).first()
All 57 tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(migration): Migrate Priority 5 controllers to SQLAlchemy 2.0
Migrate remaining controller query patterns from legacy query() API to
SQLAlchemy 2.0 select() API. This phase covers WebSocket controllers,
session management, microphone management, and authentication.
Files migrated (11 patterns):
- controllers/api/websocket.py (1 pattern)
- controllers/api/show/sessions.py (1 pattern)
- controllers/api/show/microphones.py (5 patterns)
- controllers/api/auth.py (1 pattern)
- controllers/ws_controller.py (2 patterns)
Test coverage:
- Added test_create_user_duplicate_username to test/api/test_auth.py
to explicitly test username conflict detection query
- Created test/api/test_websocket.py (2 tests)
- Created test/api/show/test_sessions.py (2 tests)
- Created test/api/show/test_microphones.py (4 tests)
- All 9 Priority 5 tests pass before and after migration
Migration pattern:
session.query(Model).filter(condition).first/all()
→ session.scalars(select(Model).where(condition)).first/all()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(migration): Migrate Priority 6 auth controllers to SQLAlchemy 2.0
Migrate remaining authentication controller query patterns from legacy
query() API to SQLAlchemy 2.0 select() API.
Files migrated (4 patterns):
- controllers/api/auth.py (4 patterns)
- Lines 116-117: User session lookup during user deletion
- Lines 126-128: User session lookup in retry loop
- Lines 162-164: User authentication by username
- Line 264: Get all users
Test coverage:
- Added test_delete_user to test/api/test_auth.py
Tests user deletion which queries for active sessions (lines 116-117, 126-128)
- Added test_get_users to test/api/test_auth.py
Tests get all users endpoint (line 264)
- Existing test_login_success and test_login_invalid_username
Already test login authentication query (lines 162-164)
All 4 tests pass before and after migration, proving behavioral equivalence.
Migration pattern:
session.query(Model).filter(condition).first/all()
→ session.scalars(select(Model).where(condition)).first/all()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(sqlalchemy): Migrate script revisions query patterns to SQLAlchemy 2.0
Migrated 2 query patterns in controllers/api/show/script/revisions.py:
Pattern 1 (POST /api/v1/show/script/revisions):
- OLD: session.query(func.max(...)).filter(...).one()[0]
- NEW: session.scalar(select(func.max(...)).where(...))
Pattern 2 (DELETE /api/v1/show/script/revisions):
- OLD: session.query(ScriptRevision).filter(...).one()
- NEW: session.scalars(select(ScriptRevision).where(...)).one()
Added endpoint-based tests:
- test_create_revision_uses_max_query: Tests POST endpoint that uses func.max()
- test_delete_revision_uses_find_first_query: Tests DELETE endpoint fallback to revision 1
All tests pass with new syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(sqlalchemy): Migrate user overrides query pattern to SQLAlchemy 2.0
Migrated 1 query pattern in models/user.py:
UserOverrides.get_by_type() method:
- OLD: session.query(UserOverrides).filter_by(...).filter_by(...).all()
- NEW: session.scalars(select(UserOverrides).where(...).where(...)).all()
Added endpoint-based tests:
- test_get_stage_direction_overrides: Tests GET endpoint that retrieves overrides
- test_get_stage_direction_overrides_no_overrides: Tests empty result set
All tests pass with new syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(sqlalchemy): Migrate API key authentication query to SQLAlchemy 2.0
Migrated 1 query pattern in utils/web/base_controller.py:
BaseAPIController.prepare() - API key authentication:
- OLD: session.query(User).filter(User.api_token.isnot(None)).all()
- NEW: session.scalars(select(User).where(User.api_token.isnot(None))).all()
Used existing tests from test/api/test_auth.py:
- test_api_token_authentication: Tests successful API key auth
- test_api_token_invalid: Tests invalid API key rejection
All tests pass with new syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(sqlalchemy): Migrate JWT secret query to SQLAlchemy 2.0
Migrated 1 query pattern in utils/web/jwt_service.py:
JWTService.get_secret() - Fetch JWT secret from database:
- OLD: session.query(SystemSettings).filter(SystemSettings.key == "jwt_secret").first()
- NEW: session.scalars(select(SystemSettings).where(SystemSettings.key == "jwt_secret")).first()
Used existing test from test/api/test_auth.py:
- test_login_success: Tests login which triggers JWT creation and secret retrieval
All tests pass with new syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(sqlalchemy): Migrate RBAC dynamic query to SQLAlchemy 2.0
Migrated 1 complex query pattern in rbac/rbac_db.py:
RBAC_DB.get_objects_for_resource() - Dynamic relationship traversal:
- OLD: session.query(self._db.get_mapper_for_table(table.fullname)).filter_by(**cols).all()
- NEW: session.scalars(select(self._db.get_mapper_for_table(table.fullname)).filter_by(**cols)).all()
Added endpoint-based test:
- test_get_objects_for_resource: Tests GET /api/v1/rbac/user/objects endpoint
which walks the database relationship graph from Show to find related resources
All tests pass with new syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix formatting
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9ff440d commit 545c4e2
46 files changed
Lines changed: 2611 additions & 297 deletions
File tree
- server
- controllers
- api
- show
- script
- digi_server
- models
- rbac
- test
- api
- show
- script
- user
- controllers
- models
- utils/web
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
48 | | - | |
49 | | - | |
50 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
111 | 112 | | |
112 | 113 | | |
113 | 114 | | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
119 | 118 | | |
120 | 119 | | |
121 | 120 | | |
| |||
124 | 123 | | |
125 | 124 | | |
126 | 125 | | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
132 | 129 | | |
133 | 130 | | |
134 | 131 | | |
| |||
162 | 159 | | |
163 | 160 | | |
164 | 161 | | |
165 | | - | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
166 | 165 | | |
167 | 166 | | |
168 | 167 | | |
| |||
262 | 261 | | |
263 | 262 | | |
264 | 263 | | |
265 | | - | |
| 264 | + | |
266 | 265 | | |
267 | 266 | | |
268 | 267 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | | - | |
25 | | - | |
26 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
167 | 168 | | |
168 | 169 | | |
169 | 170 | | |
170 | | - | |
171 | | - | |
172 | | - | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
173 | 174 | | |
174 | 175 | | |
175 | 176 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
179 | 180 | | |
180 | 181 | | |
181 | 182 | | |
182 | | - | |
183 | | - | |
184 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
185 | 186 | | |
186 | 187 | | |
187 | 188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
173 | 174 | | |
174 | 175 | | |
175 | 176 | | |
176 | | - | |
177 | | - | |
178 | | - | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
179 | 180 | | |
180 | 181 | | |
181 | 182 | | |
| |||
186 | 187 | | |
187 | 188 | | |
188 | 189 | | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
194 | 195 | | |
195 | 196 | | |
196 | 197 | | |
| |||
210 | 211 | | |
211 | 212 | | |
212 | 213 | | |
213 | | - | |
214 | | - | |
215 | | - | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
216 | 217 | | |
217 | 218 | | |
218 | 219 | | |
| |||
283 | 284 | | |
284 | 285 | | |
285 | 286 | | |
286 | | - | |
287 | | - | |
288 | | - | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
289 | 290 | | |
290 | 291 | | |
291 | 292 | | |
| |||
387 | 388 | | |
388 | 389 | | |
389 | 390 | | |
390 | | - | |
391 | | - | |
392 | | - | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
393 | 394 | | |
394 | 395 | | |
395 | 396 | | |
| |||
452 | 453 | | |
453 | 454 | | |
454 | 455 | | |
455 | | - | |
456 | | - | |
457 | | - | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
458 | 459 | | |
459 | 460 | | |
460 | 461 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
30 | 29 | | |
31 | 30 | | |
32 | 31 | | |
| |||
52 | 51 | | |
53 | 52 | | |
54 | 53 | | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
60 | 59 | | |
61 | 60 | | |
62 | 61 | | |
| |||
113 | 112 | | |
114 | 113 | | |
115 | 114 | | |
116 | | - | |
117 | | - | |
118 | | - | |
| 115 | + | |
| 116 | + | |
119 | 117 | | |
120 | 118 | | |
121 | 119 | | |
122 | 120 | | |
123 | | - | |
124 | | - | |
| 121 | + | |
125 | 122 | | |
126 | 123 | | |
127 | 124 | | |
| |||
189 | 186 | | |
190 | 187 | | |
191 | 188 | | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
197 | 192 | | |
198 | 193 | | |
199 | 194 | | |
| |||
233 | 228 | | |
234 | 229 | | |
235 | 230 | | |
236 | | - | |
237 | | - | |
238 | | - | |
| 231 | + | |
| 232 | + | |
239 | 233 | | |
240 | 234 | | |
241 | 235 | | |
242 | | - | |
243 | | - | |
| 236 | + | |
244 | 237 | | |
245 | 238 | | |
246 | 239 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | | - | |
25 | | - | |
26 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
12 | | - | |
13 | | - | |
14 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
15 | 17 | | |
16 | 18 | | |
17 | 19 | | |
| |||
0 commit comments