Repository navigation
fix(foreign): keep missing elements and raw ids of a multi reference - #116
Merged
Merged
Conversation
Upd4ting
force-pushed
the
fix/ant-1401
branch
from
November 9, 2026 14:47
77b68a0 to
e175551
Compare
Upd4ting
added a commit
that referenced
this pull request
Nov 9, 2026
## Why
A route registered as `DefaultRoutes.WithOptions(DefaultRoutes.Edit, {
index: "slug" })` should edit the row the index finds. It found that
row, then wrote the update to the row whose primary key equals `id`.
Usually no such row exists, so the edit was silently lost and the route
still answered 200 with the old row. Fixes #90.
## What changes
- `editRow` in `src/index.ts` takes the primary key (`_id`) of the row
it found, asserts it is a string, and uses it for the update and for the
read-back. Reading back by primary key also gives the right answer when
the edit changes the indexed field itself. The old read through the
index returned the unchanged row in that case.
- Without `index` the behaviour is the same as before, because the found
row's `_id` is the requested `id`.
- `docs/3.routes.md` describes the fixed behaviour in the Edit options
table.
- The verify journey `issue-90-edit-index` is renamed from "fails on
purpose" to a guard, and `features/README.md` no longer lists it as
failing.
```mermaid
flowchart LR
R["PUT /edit?id=hello (index: slug)"] --> G["Query.Get(table, id, index)"]
G -->|stored row, _id a1| W["WriteProperties + Lock"]
W --> U_old["before: table.get('hello').update"]
W --> U_new["after: table.get(stored._id).update"]
U_new --> Rd["readRow by stored._id"]
```
## Proof
`src/tests/index/edit_index.test.ts` drives the route against the
in-memory MongoDB. One row's `_id` equals the indexed value, so an
implementation that writes to the primary-key row fails the test.
Before, with `main`'s `src/index.ts`:
```text
Edit with the index option
[2026-11-08 03:46:25] [ERROR] HTTP 500 PUT /edit-index/edit?id=hello: After applying the update, the (immutable) field '_id' was found to have been altered to _id: "a1"
1) updates the row the index finds, not the row whose primary key is the id
2) answers with the edited row when the edit changes the indexed field
0 passing (93ms)
2 failing
1) Edit with the index option
updates the row the index finds, not the row whose primary key is the id:
AssertionError: expected 500 to equal 200
+ expected - actual
-500
+200
2) Edit with the index option
answers with the edited row when the edit changes the indexed field:
AssertionError: expected { slug: 'rename-me', title: 'Before' } to have deep property 'slug' of 'renamed', but got 'rename-me'
+ expected - actual
-rename-me
+renamed
```
After:
```text
Edit with the index option
✔ updates the row the index finds, not the row whose primary key is the id (53ms)
✔ answers with the edited row when the edit changes the indexed field
2 passing (152ms)
```
`pnpm run checks` prints `Checks: 8 passed, 0 failed.`
`antelopejs-verify` at `0d0e4a1`, before the rebases, every feature
`plan` lists: `route-options/issue-90-edit-index` passes. The only
failures were `issue-92-getter-mode` and `issue-93-multi-foreign`, which
#117 and #116 have since fixed on `main`. With #90 fixed too,
`features/README.md` no longer lists any journey that fails on purpose.
After the rebase onto `f4872d3`, `pnpm run checks` prints `Checks: 9
passed, 0 failed.` and both new tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A module author with a multi reference (
@Foreign(Item, undefined, true)) should get what docs/7.foreign-keys.md promises. A missing element should benull, and anoForeignroute should answer the raw ids. Instead,getanswered{}for a missing element, andnoForeignanswered objects keyed by character index, such as{"0":"i","1":"1"}.Fixes #93
What changed
Validation.Unlockran every element of a multi reference throughfromDatabase, which copies its input with{ ...raw }. That turnednullinto{}and a raw id string into a character-index object. It now converts only the elements that are objects.Validation.ClearInternalchecks only the first element to decide whether to post-process the array. So withnoForeign, a stored[null, "<id>"]still sent the string id throughtoPlainData. Its array branch now post-processes only the elements that are objects.issue-93-multi-foreignjourney failed on purpose until this fix. It is renamedforeign-multi, and its known-issue notes are removed.Proof
The test
src/tests/components/foreign_multi.test.tslands in its own commit, before the fix. Command:pnpm run build && pnpm exec ajs module test . -f dist/tests/components/foreign_multi.test.js.Before the fix (main's
src/internal/validation.ts):After the fix:
The verify-interface run against the packed interface,
ajs project devand Docker MongoDB passes every journey ofsmoke,foreign,listingandroute-optionson the head rebased onto dd3ae1c. The one exception isissue-90-edit-index, which fails on purpose until #90. Inforeign-multi,GET /orders/get?id=o3(storeditemsAll: ["i1","ghost"]) answers"itemsAll":[{"_id":"i1","name":"Widget","price":9.99},null].GET /orders/raw?id=o3answers"itemsAll":["i1","ghost"].pnpm run checksreports 9 passed and 0 failed.Not in this PR
The review turned up two older bugs. Both show the same output on main:
null#114: the list route drops a missing element of a multi reference instead of answeringnull.@Localizedfields of a multi reference target are not unlocked #115:@Localizedfields of a multi reference's target are not unlocked.