feat(frame): duplicate a frame via POST /:id/duplicate #46
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/duplicate-frame"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
POST /frame/:id/duplicate- atomic transactional copy of a frame plus its optionalframe_locationrow, into the same shoot.FOR UPDATEto serialize against concurrent deletes; shoot row is locked the same way to atomically computeMAX(number) + 1.{id}like the create endpoint.getNextFrameNumber(tx, shootId)helper, used bycreate,update, andduplicate(3 call sites of the sameCOALESCE(MAX(...))block collapsed into 1).Test plan
frame.int-spec.ts: happy path with location, no-location case, done-shoot 400, other-user 404, unknown 404, concurrent serialization.Pairs with rollnote-front feat/duplicate-frame.
Generated with Claude Code (https://claude.com/claude-code)
frame.service.ts: - extract loadOwnedFrameOrThrow, lockShootAndAssertOpen, and copyFrameLocation from duplicate(); arrow now reads as orchestration - extract prepareShootMove from update(); the shoot-move branch lives in its own helper alongside the unchanged early-exit path import.service.ts: - split parseShutterSpeedLabel into parseFiniteShutter so the validity test no longer chains a single if - extract collectCreateNewErrors from validate() so the CreateNew branch's two checks no longer compound with the outer accountTarget guard - extract loadOwnedForCounts from computeCounts(); the five ternaries over targetUserId now live in one place with an early return - split resolveShutterSpeed into readV5Shutter and readLegacyShutter so the v5/legacy fork is a clean `?? fallback` at the call site Behavior unchanged: 100/100 integration tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>fallow flags any function at CRAP >= 30, not just above. The previous refactor left readV5Shutter and frame.service.ts's update() arrow sitting at exactly 30; both are now lower. - readV5Shutter: extract hasV5ShutterFields so the v5 detection is its own predicate, dropping the inline `&& undefined` from the main function. - update(): replace the inline `frameDto.shoot != null && ... !== existing.shoot` with a shootChangeTarget helper that returns the target id or null. Behavior unchanged: 100/100 integration tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>