feat(data-access)!: drop startedAt/result/error from Preflight schema (SITES-47254)#1740
feat(data-access)!: drop startedAt/result/error from Preflight schema (SITES-47254)#1740GeezerPelletier wants to merge 6 commits into
Conversation
… (SITES-47254) These three attributes mirrored columns on the preflights table that no longer exist after the mysticat-data-service migration in SITES-47254. PostgREST select=* against the new table shape returns no result/error/ started_at, and the auto-generated getters/setters would either return null shadows of the AsyncJob source of truth (read-side) or 400 PostgREST on writes against the missing columns (write-side). After this change: - Preflight.startedAt / result / error are gone from the schema, the .d.ts surface, and the test fixtures. The auto-generated getStartedAt() / getResult() / getError() and corresponding setters are removed. - Consumers that need scan lifecycle internals fetch the joined AsyncJob via `await preflight.getAsyncJob()` and read getStartedAt() / getResult() / getError() there. - Preflight.status and Preflight.endedAt remain on the schema as a small denormalized cache for hot list-view reads; the projector keeps them in sync (see mysticat-projector-service#216 / SITES-47253). A new regression guard in preflight.schema.test.js asserts the three attributes are NOT declared on the schema, so future re-introduction fails CI here rather than only at downstream consumer Service construction time. This is a breaking change for any consumer outside spacecat-api-service that calls those getters/setters on Preflight. Today there are no such consumers — spacecat-api-service#2713 already migrated to the AsyncJob join. Conventional commits marker `feat!` triggers a major bump. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
clotton
left a comment
There was a problem hiding this comment.
This is a clean, well-scoped removal with good regression guards and thorough test cleanup. The schema change, the three absence assertions, and the comment trail are all correct. Two issues need attention before merge, and two smaller items are worth addressing.
Findings
1. BLOCKER — AsyncJob/index.d.ts is missing getStartedAt() / setStartedAt() — this PR makes them load-bearing
The updated schema comment and the PR description both redirect consumers to await preflight.getAsyncJob() then call asyncJob.getStartedAt(). The runtime getter exists (async-job.schema.js declares startedAt), but src/models/async-job/index.d.ts has no getStartedAt() or setStartedAt() declaration. Any TypeScript consumer following the migration guidance gets a compile error on asyncJob.getStartedAt(). This gap pre-existed the PR but becomes blocking the moment it is the only advertised path.
Fix: Add to src/models/async-job/index.d.ts:
getStartedAt(): string | undefined;
setStartedAt(startedAt: string): void;2. MEDIUM — getEndedAt() return type should be string | undefined, not string | null
postgrest.utils.js::normalizeModelValue() converts DB null → undefined and skips setting the key (if (normalized !== undefined) { acc[...] = ... }). The auto-generated getter returns this.record['endedAt'], which is undefined (key absent) when the column is NULL in Postgres — never null. The declaration getEndedAt(): string | null is therefore wrong for the PostgREST v3 implementation. Callers doing getEndedAt() === null will silently miss the branch on the common unfinished-job case. (The deleted declarations getStartedAt(): string | null etc. had the same bug — those are gone, but getEndedAt() survives with it.)
Fix: Change line 24 of index.d.ts to getEndedAt(): string | undefined;
3. MEDIUM — Deploy-ordering dependency is real and the companion PR is still open
spacecat-api-service/src/dto/preflight.js lines 29, 31-32 still call preflight.getStartedAt(), preflight.getResult(), preflight.getError(). The PR description correctly identifies this and says "safe to land any time after spacecat-api-service#2713 deploys." However, #2713 is still open. If this schema bump is published to npm and picked up by the API service before #2713 merges, every GET /preflights/:id request that reaches toDetailJSON will throw TypeError: preflight.getStartedAt is not a function and return 500. The description could be hardened: make the dependency explicit in the PR body (e.g., "DO NOT merge before #2713 is deployed") or add a peerDependencies / package-level note.
4. MINOR — preflight.collection.test.js mockRecord still carries startedAt (missed cleanup)
The PR correctly cleaned preflights.fixture.js and preflight.model.test.js, but preflight.collection.test.js line 40 still has startedAt: '2025-06-01T10:00:01.000Z' in its local mockRecord. The schema silently discards unknown fields on read so no test fails, but the fixture misleads future readers into thinking startedAt is still a Preflight attribute.
Fix: Delete startedAt: '2025-06-01T10:00:01.000Z' from the collection test's mockRecord.
| getStatus(): string; | ||
| getCreatedBy(): { email: string; displayName?: string }; | ||
| getStartedAt(): string | null; | ||
| getEndedAt(): string | null; |
There was a problem hiding this comment.
Return type should be string | undefined, not string | null.
postgrest.utils.js::normalizeModelValue() maps DB null → undefined and skips writing the key to this.record entirely. The auto-generated getter returns this.record['endedAt'], which is undefined (absent key) when ended_at is NULL — never null. Callers checking getEndedAt() === null will silently miss the "not yet ended" branch.
Change to:
getEndedAt(): string | undefined;|
|
||
| import type { AsyncJob, BaseCollection, BaseModel, Site } from '../index.js'; | ||
|
|
||
| // SITES-47254: startedAt/result/error live on AsyncJob — fetch via getAsyncJob(). |
There was a problem hiding this comment.
The PR description and this comment both direct consumers to asyncJob.getStartedAt(), but AsyncJob/index.d.ts has no getStartedAt() declaration.
The async-job.schema.js declares startedAt as an attribute (the runtime JS getter exists), but the TypeScript surface omits it. Any typed consumer following this migration guidance will get a compile error.
Please add to src/models/async-job/index.d.ts:
getStartedAt(): string | undefined;
setStartedAt(startedAt: string): void;There was a problem hiding this comment.
Hey @GeezerPelletier,
⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos' docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.
Verdict: Approve - clean, well-scoped schema removal with proper regression guards and clear migration path documented.
Complexity: MEDIUM - medium diff size (schema attribute removal + corresponding test updates).
Changes: Drops startedAt/result/error attributes from the Preflight schema, centralizing lifecycle data on the joined AsyncJob entity (5 files).
Note: CI checks are currently failing - the integration test job cannot pull mysticat-data-service:v5.24.1 from ECR (image not found). This is an infrastructure issue, not a defect introduced by this PR. Unit tests and lint pass. Resolve the image availability before merge.
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 2m 6s | Cost: $4.63 | Commit: 8c0d42afd56bd51b62132390da07f5082dea82ad
If this code review was useful, please react with 👍. Otherwise, react with 👎.
… fix return type (review #1740) Addresses Chris's review on PR #1740: Blocker — `AsyncJob/index.d.ts` was missing `getStartedAt()` / `setStartedAt()` declarations. The runtime getters exist (`async-job.schema.js` declares the attribute), but the TS surface omitted them — this PR's schema comment and the PR description redirect consumers to `await preflight.getAsyncJob()` then `asyncJob.getStartedAt()`, so the gap was pre-existing but became blocking the moment this PR makes that the only advertised path. Added both declarations with return type `string | undefined` (DB NULL → JS undefined via normalizeModelValue). Medium — `Preflight.getEndedAt()` return was still `string | null` from the original .d.ts. `normalizeModelValue` maps DB NULL to JS `undefined` on read (the key is skipped on `this.record` entirely), so callers checking `=== null` would silently miss the unfinished-scan branch. Changed to `string | undefined`. Added a header comment on `AsyncJob/index.d.ts` documenting the convention; the Preflight type points at it. Minor — Removed the stale `startedAt: '2025-06-01T10:00:01.000Z'` from the `mockRecord` in `preflight.collection.test.js`. Schema silently discards unknown fields on read so no test was failing, but the residue misled future readers into thinking startedAt was still a Preflight attribute. Verified: 55 unit tests passing across preflight + async-job suites. Lint clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
This PR will trigger a patch release when merged. |
|
Thanks @clotton — all four findings addressed in Blocker #1 — Missing
|
clotton
left a comment
There was a problem hiding this comment.
All findings from the prior review resolved:
AsyncJob/index.d.tsmissing lifecycle declarations —getStartedAt(),getEndedAt(),setStartedAt(),setEndedAt()all added with correctstring | undefinedreturn types (not| null), matching thenormalizeModelValueDB-null → undefined runtime contract. The header comment explaining why| undefinedis correct is a useful guard against future=== nullmistakes.Preflight/index.d.tswrong return type —getEndedAt()corrected tostring | undefined;getStartedAt(),getResult(),getError()removed cleanly with a redirect comment togetAsyncJob().- Stale
startedAtin collection test — removed from the mockRecord. - Schema regression guard — the new
'attribute surface'describe block pins thatstartedAt/result/errorare absent from the Preflight schema, preventing silent re-introduction.
LGTM.
Summary
Schema-layer half of SITES-47254. Drops the
startedAt,result, anderrorattributes from thePreflightschema — they mirror columns on thepreflightstable that no longer exist after the mysticat-data-service migration.After this change, lifecycle internals live only on the joined
AsyncJob:Preflight.statusandPreflight.endedAtremain on the schema as a small denormalized cache for hot list-view reads — the projector (mysticat-projector-service#216) keeps them in sync.Why the bump is a major (
feat!)Removing schema attributes removes the auto-generated
getStartedAt()/getResult()/getError()and corresponding setters from thePreflightmodel. Any downstream consumer that calls those onPreflightwill throwTypeError: preflight.getResult is not a functionafter upgrading.Today the only known consumer is
spacecat-api-service, and PR #2713 has already migratedgetPreflightByIdto source those fields from the joinedAsyncJob. No other usages found via grep across the workspace.Changes
src/models/preflight/preflight.schema.js— dropstartedAt,result,errorattributes. Updated header comment explaining the AsyncJob ownership.src/models/preflight/index.d.ts— drop thegetStartedAt/getResult/getErrorand corresponding setter declarations;getEndedAtreturn is nowstring | undefined(not| null) to match the runtime —normalizeModelValuemaps DB NULL → JSundefined.src/models/async-job/index.d.ts— addgetStartedAt()/setStartedAt()/getEndedAt()/setEndedAt()declarations (runtime getters exist via the schema; the TS surface was missing them — this PR makes them load-bearing since consumers are now redirected toasyncJob.getStartedAt()). Return types usestring | undefinedfor the same reason; header comment documents the convention.test/fixtures/preflights.fixture.js— strip the dropped fields from the three fixture rows; one-line comment explains where the lifecycle data lives now.test/unit/models/preflight/preflight.model.test.js— drop the threestartedAt/result/errordescribe blocks and the corresponding fields frommockRecord.test/unit/models/preflight/preflight.collection.test.js— drop stalestartedAtfield frommockRecord(no test was failing because the schema discards unknown fields on read, but the residue misled future readers).test/unit/models/preflight/preflight.schema.test.js— drop the now-irrelevanterror attributevalidate-suite, and add aregression guard for SITES-47254block asserting the three attributes are NOT declared (re-introduction fails CI here rather than only at downstreamnew electrodb.Service(...)time).Verification
npx mocha packages/spacecat-shared-data-access/test/unit/models/preflight/*.test.js packages/spacecat-shared-data-access/test/unit/models/async-job/*.test.js— 55 passing, 0 failing, including the new regression guards.npx mocha --timeout 30000 packages/.../electrodb-service-construction.test.js— passes; the cross-entity Service construction guard validates the full registry still constructs cleanly after the schema change.Testjob — currently failing on an ECR image manifest issue (mysticat-data-service:v5.24.1 not found) that's unrelated to this PR. Tracking separately if it doesn't auto-resolve on the next CI run.Deploy ordering
preflight.getResult()/getError()/getStartedAt()— migrates toasyncJob.getXxx().Preflight.npm i @adobe/spacecat-shared-data-access@<new-major>when convenientThe data-service column drop (mysticat-data-service#746) is order-independent relative to this PR — it can land before or after; either ordering keeps the model and DB in sync (both stop carrying the columns).
Related
Preflight.getResult()/getError()/getStartedAt()onto the AsyncJob joinpreflightstable{status, ended_at}onto preflights now🤖 Generated with Claude Code