Skip to content

core: token events#1179

Closed
renbaoshuo wants to merge 1 commit into
hydro-dev:masterfrom
renbaoshuo:token-events
Closed

core: token events#1179
renbaoshuo wants to merge 1 commit into
hydro-dev:masterfrom
renbaoshuo:token-events

Conversation

@renbaoshuo

@renbaoshuo renbaoshuo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Token operations now support lifecycle event hooks, enabling integration with custom workflows during token creation, updates, and deletion.
  • Changes

    • Token deletion behavior improved to handle per-token operations with better validation.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR extends the TokenModel to emit lifecycle events around token persistence operations. The bus event type contract (EventMap) is extended with six new token/* event signatures (before-add, add, before-update, update, before-del, del). TokenModel's add() and update() methods now emit serialized before-* hooks before database operations and parallel hooks after. The del() method is refactored to fetch the token first and return a boolean indicating deletion success, while delByUid() switches from bulk deleteMany to per-document async deletion, returning the count of deleted tokens and emitting hooks for each one.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'core: token events' directly and clearly describes the main change: adding event emission around token persistence operations in the TokenModel.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/hydrooj/src/model/token.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/hydrooj/src/service/bus.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/hydrooj/src/model/token.ts (1)

80-85: ⚡ Quick win

Inconsistent hook type: before-del uses parallel instead of serial.

The token/before-add and token/before-update hooks use bus.serial(), which allows listeners to run sequentially and potentially block/modify the operation. However, token/before-del uses bus.parallel(), which runs listeners concurrently without ordering guarantees.

For consistency with other "before-*" hooks in this file (and to allow potential cancellation/validation logic), consider using bus.serial:

-        await bus.parallel('token/before-del', tokenId, tdoc);
+        await bus.serial('token/before-del', tokenId, tdoc);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/model/token.ts` around lines 80 - 85, The deletion hook
uses bus.parallel('token/before-del', tokenId, tdoc) which is inconsistent with
the other pre-hooks (token/before-add and token/before-update) that use
bus.serial; change the before-delete call to use bus.serial('token/before-del',
tokenId, tdoc) so listeners run sequentially and can perform
validation/cancellation, keeping the subsequent await
TokenModel.coll.deleteOne({ _id: tokenId, tokenType }) and
bus.parallel('token/del', tokenId, tdoc) behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrooj/src/model/token.ts`:
- Around line 74-75: The code currently emits the 'token/update' event with res
even when findOneAndUpdate returned null; change the logic in the update routine
(the block that calls await bus.parallel('token/update', tokenId, res)) to check
that res is not null before emitting—i.e., only call
bus.parallel('token/update', tokenId, res) when res !== null (or handle the null
case explicitly by emitting a different event or logging), so listeners
expecting a TokenDoc for the 'token/update' event never receive null.
- Around line 114-122: The current delByUid emits bus events even when deleteOne
returns deletedCount === 0; change the flow in TokenModel.delByUid so you await
bus.serial('token/before-del', tdoc._id, tdoc) (for consistency with del()),
then call this.coll.deleteOne({ _id: tdoc._id }) and only if r.deletedCount > 0
emit bus.parallel('token/del', tdoc._id, tdoc) and return true; if deletedCount
is 0 return false (do not emit token/del). Keep the outer Promise.all mapping
over tdocs and the final reduce to count successes.

In `@packages/hydrooj/src/service/bus.ts`:
- Around line 102-107: The event signature for 'token/update' currently requires
a TokenDoc but the emitter in token.ts calls bus.parallel('token/update',
tokenId, res) where res can be TokenDoc | null; update the Bus type entry for
'token/update' to accept (tokenId: string, tdoc: TokenDoc | null) => VoidReturn
(or alternatively add a null-guard in the caller before calling bus.parallel in
token.ts), ensuring references to TokenDoc and the 'token/update' handler match
the actual nullable return from findOneAndUpdate.

---

Nitpick comments:
In `@packages/hydrooj/src/model/token.ts`:
- Around line 80-85: The deletion hook uses bus.parallel('token/before-del',
tokenId, tdoc) which is inconsistent with the other pre-hooks (token/before-add
and token/before-update) that use bus.serial; change the before-delete call to
use bus.serial('token/before-del', tokenId, tdoc) so listeners run sequentially
and can perform validation/cancellation, keeping the subsequent await
TokenModel.coll.deleteOne({ _id: tokenId, tokenType }) and
bus.parallel('token/del', tokenId, tdoc) behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 119bbf46-6463-4194-839c-c5799ee1d713

📥 Commits

Reviewing files that changed from the base of the PR and between 809d1ac and 7ec29aa.

📒 Files selected for processing (2)
  • packages/hydrooj/src/model/token.ts
  • packages/hydrooj/src/service/bus.ts

Comment on lines +74 to 75
await bus.parallel('token/update', tokenId, res);
return res;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

res may be null; event emits unconditionally.

findOneAndUpdate returns null if no document matches the filter. The token/update event is emitted regardless, potentially passing null to listeners expecting a TokenDoc. Consider guarding:

+        if (res) {
             await bus.parallel('token/update', tokenId, res);
+        }
         return res;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await bus.parallel('token/update', tokenId, res);
return res;
if (res) {
await bus.parallel('token/update', tokenId, res);
}
return res;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/model/token.ts` around lines 74 - 75, The code currently
emits the 'token/update' event with res even when findOneAndUpdate returned
null; change the logic in the update routine (the block that calls await
bus.parallel('token/update', tokenId, res)) to check that res is not null before
emitting—i.e., only call bus.parallel('token/update', tokenId, res) when res !==
null (or handle the null case explicitly by emitting a different event or
logging), so listeners expecting a TokenDoc for the 'token/update' event never
receive null.

Comment on lines +114 to 122
static async delByUid(uid: number) {
const tdocs = await TokenModel.coll.find({ uid }).toArray();
return await Promise.all(tdocs.map(async (tdoc) => {
await bus.parallel('token/before-del', tdoc._id, tdoc);
const r = await this.coll.deleteOne({ _id: tdoc._id });
await bus.parallel('token/del', tdoc._id, tdoc);
return !!r.deletedCount;
})).then((r) => r.reduce((prev, cur) => prev + (cur ? 1 : 0), 0));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Events fire even when deletion fails due to concurrent removal.

If a token is deleted between find() and deleteOne() (e.g., by TTL expiry or another process), token/before-del and token/del events are still emitted even though deletedCount is 0. This could mislead listeners.

Consider emitting token/del only when deletion succeeds:

         return await Promise.all(tdocs.map(async (tdoc) => {
             await bus.parallel('token/before-del', tdoc._id, tdoc);
             const r = await this.coll.deleteOne({ _id: tdoc._id });
-            await bus.parallel('token/del', tdoc._id, tdoc);
+            if (r.deletedCount) {
+                await bus.parallel('token/del', tdoc._id, tdoc);
+            }
             return !!r.deletedCount;
         })).then((r) => r.reduce((prev, cur) => prev + (cur ? 1 : 0), 0));

Also, for consistency with del(), consider using bus.serial for token/before-del here.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async delByUid(uid: number) {
const tdocs = await TokenModel.coll.find({ uid }).toArray();
return await Promise.all(tdocs.map(async (tdoc) => {
await bus.parallel('token/before-del', tdoc._id, tdoc);
const r = await this.coll.deleteOne({ _id: tdoc._id });
await bus.parallel('token/del', tdoc._id, tdoc);
return !!r.deletedCount;
})).then((r) => r.reduce((prev, cur) => prev + (cur ? 1 : 0), 0));
}
static async delByUid(uid: number) {
const tdocs = await TokenModel.coll.find({ uid }).toArray();
return await Promise.all(tdocs.map(async (tdoc) => {
await bus.parallel('token/before-del', tdoc._id, tdoc);
const r = await this.coll.deleteOne({ _id: tdoc._id });
if (r.deletedCount) {
await bus.parallel('token/del', tdoc._id, tdoc);
}
return !!r.deletedCount;
})).then((r) => r.reduce((prev, cur) => prev + (cur ? 1 : 0), 0));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/model/token.ts` around lines 114 - 122, The current
delByUid emits bus events even when deleteOne returns deletedCount === 0; change
the flow in TokenModel.delByUid so you await bus.serial('token/before-del',
tdoc._id, tdoc) (for consistency with del()), then call this.coll.deleteOne({
_id: tdoc._id }) and only if r.deletedCount > 0 emit bus.parallel('token/del',
tdoc._id, tdoc) and return true; if deletedCount is 0 return false (do not emit
token/del). Keep the outer Promise.all mapping over tdocs and the final reduce
to count successes.

Comment on lines +102 to +107
'token/before-add': (payload: any) => VoidReturn;
'token/add': (tdoc: TokenDoc) => VoidReturn;
'token/before-update': (tokenId: string, payload: any) => VoidReturn;
'token/update': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/before-del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Type signature for token/update may not match actual usage.

In token.ts line 74, bus.parallel('token/update', tokenId, res) is called where res is the result of findOneAndUpdate, which can be TokenDoc | null. The event signature here expects TokenDoc, not TokenDoc | null.

Consider updating the signature to handle the nullable case:

-    'token/update': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
+    'token/update': (tokenId: string, tdoc: TokenDoc | null) => VoidReturn;

Alternatively, guard against null in token.ts before emitting the event.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'token/before-add': (payload: any) => VoidReturn;
'token/add': (tdoc: TokenDoc) => VoidReturn;
'token/before-update': (tokenId: string, payload: any) => VoidReturn;
'token/update': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/before-del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/before-add': (payload: any) => VoidReturn;
'token/add': (tdoc: TokenDoc) => VoidReturn;
'token/before-update': (tokenId: string, payload: any) => VoidReturn;
'token/update': (tokenId: string, tdoc: TokenDoc | null) => VoidReturn;
'token/before-del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
'token/del': (tokenId: string, tdoc: TokenDoc) => VoidReturn;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrooj/src/service/bus.ts` around lines 102 - 107, The event
signature for 'token/update' currently requires a TokenDoc but the emitter in
token.ts calls bus.parallel('token/update', tokenId, res) where res can be
TokenDoc | null; update the Bus type entry for 'token/update' to accept
(tokenId: string, tdoc: TokenDoc | null) => VoidReturn (or alternatively add a
null-guard in the caller before calling bus.parallel in token.ts), ensuring
references to TokenDoc and the 'token/update' handler match the actual nullable
return from findOneAndUpdate.

@renbaoshuo renbaoshuo closed this Jun 15, 2026
@renbaoshuo renbaoshuo deleted the token-events branch June 15, 2026 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant