fix: accept numeric string node ids when serializing request files - #8367
fix: accept numeric string node ids when serializing request files#8367maia-andre wants to merge 1 commit into
Conversation
The Files sidebar can hand AppFilesTab a node whose id is a numeric string, for example a file copied in the Files app and opened in the sidebar before the list is refreshed. AppFilesTab stores that value as nodeId unchanged, and serializeRequestFile() only accepted numbers, so the signature request was sent without the file reference and the API answered 422 "File or files parameter is required". Normalize nodeId, fileId and id through one helper that accepts a positive integer or a numeric string. Non-numeric envelope placeholders such as 'temp-node' are still left out of the payload. Resolves: LibreSign#8363 Assisted-by: Claude Code:claude-opus-5 Signed-off-by: André Maia <andrefnkmm@gmail.com>
|
Codecov Report❌ Patch coverage is
... and 220 files with indirect coverage changes 🚀 New features to boost your workflow:
|
vitormattos
left a comment
There was a problem hiding this comment.
I think we need to follow the ID value through the complete flow before normalizing nodeId, fileId, and id with the same helper.
LibreSign already completed the migration to the current meaning where:
nodeId/signedNodeIdidentify Nextcloud nodes;id/fileId/parentFileIdidentify records inlibresign_file.
If any current code still uses fileId for a Nextcloud node ID, that should be treated as a leftover from the old model and corrected, not as a valid alternative meaning.
We already found at least one example of this kind of leftover: RequestSignatureService still has a local $fileId whose value comes from Node::getId() or file.nodeId, and it is then passed to getByNodeId(). In other places, fileId correctly goes to getById().
Because of this history, I do not think we should infer what an ID means only from its property name. We need to follow the actual value.
Looking more about this, I found an important Nextcloud change to consider. In the current @nextcloud/files API, Node.id is string | undefined. Node.fileid is the legacy numeric property and is deprecated. The string representation is intentional because Nextcloud is moving to 64-bit snowflake IDs, which cannot always be represented safely as JavaScript numbers.
Nextcloud documents Snowflake IDs here:
https://docs.nextcloud.com/server/stable/developer_manual/digging_deeper/snowflake_ids.html
They were added in Nextcloud 33 and are 64-bit identifiers. The Nextcloud 33 developer release notes also mention that APIs migrated to Snowflake IDs use strings instead of integers:
https://docs.nextcloud.com/server/stable/developer_manual/release_notes/previous/upgrade_to_33.html
Could we first trace the exact #8363 flow and document where the value comes from and what it represents at each step?
For example:
Nextcloud Node -> tab.ts -> AppFilesTab -> files store -> serializeRequestFile() -> request-signature API -> backend lookup
For each value used as nodeId, fileId, or id in this flow, please verify:
- where the value originates;
- whether it identifies a Nextcloud node or a
libresign_filerow; - whether it is renamed or transformed on the way;
- which backend lookup finally consumes it (
getByNodeId(),getById(), NextcloudgetById(), etc.).
If this flow still uses fileId for a Nextcloud node ID anywhere, that should be corrected as part of the leftover cleanup from the completed migration.
The fix should happen at the point where the representation first becomes incorrect.
In particular, converting a Nextcloud Node.id string with Number() is not safe for future snowflake IDs. A value above Number.MAX_SAFE_INTEGER can silently become a different ID.
I would therefore avoid making serializeRequestFile() generally accept and convert numeric strings until we know which representations are valid for each field.
Please also avoid using an artificial state such as nodeId: 'temp-node' to define the domain model unless production code can really produce that value. The regression tests should reproduce the real sidebar data path as closely as possible.
While following this flow, please also check the test coverage of every method or branch that needs to be changed. If the relevant behavior is not already covered, please add a focused test before or together with the change. The tests should protect the real ID semantics and the complete regression path, not only the final serializer output.
This PR does not need to audit every ID in LibreSign. It should trace and fix the complete #8363 path. If that investigation exposes other leftovers from the old fileId = Nextcloud node ID model, we can handle those in a separate cleanup issue.
|
Thanks — agreed on all three points, and the trace changed my view of where the fix belongs. Below is the #8363 path on Where the string comes from
Then, in The path
Where the fix belongsTwo places make the representation incorrect, and neither should convert with
One decision I need from you — the API contract.
I lean to (a); it is the honest description of what the endpoint accepts, and it is one line plus generated files. Tests (real sidebar data, no
|
|
Thanks, this trace is much clearer, and the proposed direction looks consistent with what I found as well. I checked the flow against the current LibreSign code and the current Nextcloud contracts. The important distinction seems to be:
So the flow that makes the most sense for LibreSign is:
This also means converting the value with I would also treat the Your trace of the LibreSign path also looks correct to me:
I think the typing is especially important here. On the frontend, the type should reflect the real On the backend, I would prefer to validate and normalize the HTTP value once at a clear boundary, then let strong The intended model would be:
If the current data structure makes it difficult to propagate the normalized value, a small shared normalizer would be preferable to repeated casts in different methods. Because of that, I think the safer frontend change would be to keep the fix specific to For the API input, I think we can make the decision here: Could you also check what OpenAPI is actually generated from the proposed Internally, PHP and database values can remain I would keep changing The test plan looks good. Using a real node ID above I also agree with leaving the other leftovers out of this PR. Since some old For backports, I would check affectedness rather than only whether the patch cherry-picks cleanly.
With that, the direction of the rewrite looks good to me. |
Resolves: #8363
📝 Summary
serializeRequestFile()insrc/store/files.jsonly acceptednodeId,fileIdandidwhen they were numbers. The Files sidebar can handAppFilesTaba node whose id is a numeric string —tab.tsmapsnode.fileid ?? node.id, andAppFilesTab.update()stores it asnodeIdunchanged — so for a file copied in the Files app and opened in the sidebar before the list is refreshed, the serializer returnednull, the request went out withoutfile, and the API answered422 "File or files parameter is required". The store's own types already declarenodeId?: number | string | null; the serializer was the one place not honouring that.The fix normalizes the three ids through one helper,
toPositiveIntegerId(), which accepts a positive integer or a numeric string (/^\d+$/). Non-numeric envelope placeholders such as'temp-node'still produce no file reference, which the existing envelope test relies on.🧪 How to test
Unit tests (
src/tests/store/files.spec.ts):includes file.nodeId when the temporary file carries a numeric string nodeId— fails onmainwithexpected null to deeply equal { nodeId: 12345 }, passes with the fix;serializes envelope files whose nodeId is a numeric string— fails onmainwith[ { nodeId: 22 } ](the string entry was dropped), passes with the fix;does not turn a non-numeric envelope nodeId into a file reference— guards the'temp-node'case; passes before and after.vitest run: 253 files, 3178 tests passed.eslintclean on both files.Manual, as reported in the issue: copy a PDF in Files, open the right sidebar on the copy, use the Request signature tab, add an email signer and save — the
POST /apps/libresign/api/v1/request-signaturepayload now carriesfile: { nodeId }.⚙️ API / Back‑end changes
Frontend only. No API change.
🚧 Backport
Bug present in 14.1.0 (stable34). The cherry-pick applies cleanly to
stable32,stable33,stable34andstable35(checked locally on each branch).✅ Checklist
🤖 AI (if applicable)