fix(api): un-quote orderBy column refs in sandbox lifecycle crons#672
fix(api): un-quote orderBy column refs in sandbox lifecycle crons#672lilongen wants to merge 1 commit into
Conversation
The auto-stop / auto-archive / auto-delete cron queries passed `sandbox."lastBackupAt"` / `activity."lastActivityAt"` to QueryBuilder.orderBy. TypeORM treats the alias.property form and applies its own identifier quoting; the pre-quoted column then gets double-escaped into invalid SQL, crashing the cron. Passing the plain alias.property lets TypeORM map and quote it correctly (CustomNamingStrategy keeps the camelCase column name). Verified against dev RDS (in-VPC API): the lifecycle crons run without the TypeORM crash. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR removes double-quote wrapping from camelCased column references in TypeORM ChangesSandbox Query OrderBy Syntax
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
lile seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
1 similar comment
|
lile seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates several TypeORM orderBy clauses to remove explicit quoting around camelCase column names.
Changes:
- Changed
orderBy('sandbox."lastBackupAt"')toorderBy('sandbox.lastBackupAt')in multiple queries. - Changed
orderBy('activity."lastActivityAt"')toorderBy('activity.lastActivityAt')in multiple queries.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .andWhere('sandbox."autoStopInterval" != 0') | ||
| .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoStopInterval"') | ||
| .orderBy('sandbox."lastBackupAt"', 'ASC') | ||
| .orderBy('sandbox.lastBackupAt', 'ASC') |
| .andWhere('sandbox."autoArchiveInterval" != 0') | ||
| .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoArchiveInterval"') | ||
| .orderBy('sandbox."lastBackupAt"', 'ASC') | ||
| .orderBy('sandbox.lastBackupAt', 'ASC') |
| .andWhere('sandbox."autoDeleteInterval" >= 0') | ||
| .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoDeleteInterval"') | ||
| .orderBy('activity."lastActivityAt"', 'ASC') | ||
| .orderBy('activity.lastActivityAt', 'ASC') |
| .andWhere('sandbox."desiredState"::text != sandbox.state::text') | ||
| .andWhere('sandbox."desiredState"::text != :archived', { archived: SandboxDesiredState.ARCHIVED }) | ||
| .orderBy('activity."lastActivityAt"', 'DESC', 'NULLS LAST') | ||
| .orderBy('activity.lastActivityAt', 'DESC', 'NULLS LAST') |
…r infra-local Re-add the OIDC user-create default-region fix that was split to PR boxlite-ai#668. Removing it from this branch broke infra-local: a fresh Dex login creates a Personal org with defaultRegionId=NULL, and dashboard create-sandbox then fails with HTTP 428 "organization does not have a default region" (caught by E2E). Like the runner arm64 (boxlite-ai#671) and cron orderby (boxlite-ai#672) fixes, this is a general fix the local stack depends on, so it stays in-branch until boxlite-ai#668 lands on main; it drops from this PR's diff on the next rebase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
i don't think the double-escaping happens, so i'm not sure this fix is needed. with the installed typeorm (0.3.27), order by isn't quoted at build time — the whole query gets one final pass through i replayed typeorm's exact regex (using its own
both are valid sql. same story for the corroborating: the the pr verified the new code runs but didn't show the old code crashing. could the dev RDS crash have come from something else (a join / the stream query) rather than order by? if you still have the failing sql or stack trace, drop it here — that'd settle it. otherwise this looks like a cosmetic no-op. |
|
Closing this — resolving it via dependency pinning instead of an app-source change. Root cause (thanks @DorianZheng — your skepticism was right): the quoted Why the version differed: Fix instead: pin Note: the The un-quote is still correct hygiene, so if/when we intentionally move to 0.3.29+ we should reapply it first — but that's not needed now. |
…sion) apps/yarn.lock is gitignored, so the floating "^0.3.20" range re-resolved per environment: local picked 0.3.29 while dev/prod CI built 0.3.27/0.3.28. typeorm 0.3.29 widened the DISTINCT-subquery pagination trigger in SelectQueryBuilder.executeEntitiesAndRawResults from `(skip || take)` to `(skip || take || offset || limit)`. The sandbox lifecycle crons use innerJoin + quoted orderBy + .limit(100) + getMany, so on 0.3.29 the .limit() now routes them through createOrderByCombinedWithSelectExpression, which fails to resolve the quoted property name and throws `Cannot read properties of undefined (reading 'databaseName')` before any SQL reaches the DB. Pinning to exactly 0.3.28 aligns local with what dev/prod actually run and stops the caret from drifting back to 0.3.29. Do not bump to 0.3.29+ until the quoted-orderBy usage in sandbox.manager.ts is changed to bare property refs (cf. PR boxlite-ai#672). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion) apps/yarn.lock is gitignored, so the floating "^0.3.20" range re-resolved per environment: local picked 0.3.29 while dev/prod CI built 0.3.27/0.3.28. typeorm 0.3.29 widened the DISTINCT-subquery pagination trigger in SelectQueryBuilder.executeEntitiesAndRawResults from `(skip || take)` to `(skip || take || offset || limit)`. The sandbox lifecycle crons use innerJoin + quoted orderBy + .limit(100) + getMany, so on 0.3.29 the .limit() now routes them through createOrderByCombinedWithSelectExpression, which fails to resolve the quoted property name and throws `Cannot read properties of undefined (reading 'databaseName')` before any SQL reaches the DB. Pinning to exactly 0.3.28 aligns local with what dev/prod actually run and stops the caret from drifting back to 0.3.29. Do not bump to 0.3.29+ until the quoted-orderBy usage in sandbox.manager.ts is changed to bare property refs (cf. PR boxlite-ai#672). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
The sandbox auto-stop / auto-archive / auto-delete cron queries passed pre-quoted column refs (
sandbox."lastBackupAt",activity."lastActivityAt") toQueryBuilder.orderBy(...). TypeORM interprets thealias.propertyform and applies its own identifier quoting, so the already-quoted column gets double-escaped into invalid SQL and the cron crashes.Fix: pass the plain
alias.propertyand let TypeORM map + quote it.CustomNamingStrategy(extendsDefaultNamingStrategy) preserves the camelCase column name, so the generated SQL is correct.Why a separate PR
Extracted from the infra-local stack PR (#595) per review — it's a general API fix to the lifecycle crons, unrelated to the local-dev stack.
Verification
Verified against dev RDS (the in-VPC API box): the lifecycle crons run without the TypeORM crash. (No unit test added — a meaningful test requires a live Postgres + full TypeORM entity metadata; this codebase has no integration-test harness for the cron query builders.)
🤖 Generated with Claude Code
Summary by CodeRabbit