-
Notifications
You must be signed in to change notification settings - Fork 0
Implement batch name resolution for emails #418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a batched user-info resolver: backend POST /api/v1/users/batchResolveInfo that queries DynamoDB with batching and retry/backoff; frontend introduces UserResolverProvider and NameOptionalUserCard to batch, cache, and display resolved names across multiple UI pages and tests. Changes
Sequence DiagramsequenceDiagram
autonumber
participant UI as NameOptionalUserCard
participant Provider as UserResolverProvider
participant Cache as Local Cache
participant API as /api/v1/users/batchResolveInfo
participant Backend as batchGetUserInfo (DynamoDB)
UI->>Provider: requestUser(email)
Provider->>Cache: lookup(email)
alt cached
Cache-->>Provider: name or NO_NAME_FOUND
Provider-->>UI: return result
else not cached
Provider->>Provider: enqueue email (debounce window)
Note over Provider: batch formed after delay
Provider->>API: POST { emails: [...] }
API->>Backend: batchGetUserInfo(emails)
Backend->>Backend: BatchGetItemCommand (<=100) with projection
alt UnprocessedKeys present
Backend->>Backend: emit TransactionCanceledException → retryDynamoTransactionWithBackoff
end
Backend-->>API: mapping { email: { firstName?, lastName? } }
API-->>Provider: mapping
Provider->>Cache: store results (including NO_NAME_FOUND)
Provider-->>UI: resolve pending requests
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
💰 Infracost reportMonthly estimate generatedThis comment will be updated when code changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ui/pages/roomRequest/ViewRoomRequest.page.tsx (1)
448-510: Add a stable ReactkeyonTimeline.Itemin themap
data.updates.map((x) => (<Timeline.Item ...>))currently renders list items without akey, which React (and Biome) warn against and can cause reconciliation glitches.You can fix this by adding a deterministic key, e.g.:
- {data.updates.map((x) => ( - <Timeline.Item + {data.updates.map((x) => ( + <Timeline.Item + key={`${x.createdAt}-${x.status}`} bullet={getStatusIcon(x.status)} title={<Text size="md">{formatStatus(x.status)}</Text>} > {x.createdBy && ( <NameOptionalUserCard email={x.createdBy} /> )}The rest of the changes here (NameOptionalUserCard usage, spacing tweaks, and Text wrappers) look good.
🧹 Nitpick comments (2)
src/ui/pages/tickets/ViewTickets.page.tsx (1)
363-379: Verify resolver provider wrapping and consider keyboard accessibilityUsing
<NameOptionalUserCard>in the Email column is a nice enhancement, but it depends onUserResolverProvider. Please confirm thatViewTicketsPageis rendered under that provider (e.g. viaApp.tsx), otherwiseuseUserResolverwill throw at runtime.Also, the clickable area for copying the ticket ID is a
BoxwithonClickand pointer cursor. For better accessibility, consider using a<button>/ActionIconor adding keyboard handlers (onKeyDownfor Enter/Space andtabIndex={0}) so keyboard and screen‑reader users can trigger the copy behavior as well.src/ui/components/NameOptionalCard/index.tsx (1)
51-106: Consider cleaning up batch timeout on unmount and differentiating transient failuresThe batching / caching logic looks solid overall, but two small points:
batchTimeout.currentis never cleared on provider unmount, so a pending timeout may still fire and callsetUserCacheafter the component is gone, which can trigger “setState on unmounted component” warnings.- In the
catchblock you permanently mark allemailsToFetchasNO_NAME_FOUND. That makes sense for protecting the API, but it also means transient network/Dynamo issues can prevent any future resolution for those emails until a full reload.If this provider is long‑lived (e.g. app‑wide), consider:
- Adding a
useEffectcleanup that clearsbatchTimeout.currenton unmount.- Optionally distinguishing “hard” failures (e.g. 4xx) from transient ones and allowing retry on the latter (e.g. by not caching
NO_NAME_FOUNDin the error case, or by expiring those entries after some time).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (18)
src/api/functions/uin.ts(2 hunks)src/api/routes/user.ts(2 hunks)src/common/types/user.ts(1 hunks)src/ui/App.tsx(2 hunks)src/ui/components/NameOptionalCard/index.tsx(1 hunks)src/ui/pages/apiKeys/ManageKeys.page.tsx(1 hunks)src/ui/pages/apiKeys/ManageKeysTable.test.tsx(3 hunks)src/ui/pages/apiKeys/ManageKeysTable.tsx(2 hunks)src/ui/pages/iam/GroupMemberManagement.test.tsx(2 hunks)src/ui/pages/iam/GroupMemberManagement.tsx(2 hunks)src/ui/pages/logs/LogRenderer.test.tsx(3 hunks)src/ui/pages/logs/LogRenderer.tsx(3 hunks)src/ui/pages/membershipLists/ExternalMemberListManagement.tsx(3 hunks)src/ui/pages/roomRequest/ViewRoomRequest.page.tsx(3 hunks)src/ui/pages/stripe/CurrentLinks.test.tsx(2 hunks)src/ui/pages/stripe/CurrentLinks.tsx(2 hunks)src/ui/pages/tickets/ViewTickets.page.tsx(4 hunks)tests/live/user.test.ts(1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
src/ui/pages/roomRequest/ViewRoomRequest.page.tsx
[error] 454-457: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🪛 ESLint
src/api/functions/uin.ts
[error] 9-9: Unexpected use of file extension "js" for "api/types.js"
(import/extensions)
[error] 10-10: Unexpected use of file extension "js" for "api/utils.js"
(import/extensions)
src/ui/components/NameOptionalCard/index.tsx
[error] 1-1: Resolve error: EACCES: permission denied, open '/kLAsoYtwGa'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 12-12: Missing file extension for "../AuthContext"
(import/extensions)
src/common/types/user.ts
[error] 13-13: Insert ,
(prettier/prettier)
[error] 14-15: Replace ⏎ with ;
(prettier/prettier)
[error] 17-18: Delete ⏎
(prettier/prettier)
[error] 21-21: Insert ,
(prettier/prettier)
[error] 22-22: Insert ,
(prettier/prettier)
[error] 25-25: Replace typeof·batchResolveUserInfoResponse with ⏎··typeof·batchResolveUserInfoResponse⏎
(prettier/prettier)
src/api/routes/user.ts
[error] 16-16: Unexpected use of file extension "js" for "common/types/user.js"
(import/extensions)
[error] 21-21: Unexpected use of file extension "js" for "api/functions/uin.js"
(import/extensions)
tests/live/user.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/VVuTkMHkWc'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 2-2: Unexpected use of file extension "js" for "./utils.js"
(import/extensions)
[error] 3-3: './utils.js' import is duplicated.
(no-duplicate-imports)
[error] 3-3: Unexpected use of file extension "js" for "./utils.js"
(import/extensions)
src/ui/App.tsx
[error] 11-11: Missing file extension for "./components/NameOptionalCard"
(import/extensions)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build Application
- GitHub Check: Run Unit Tests
🔇 Additional comments (15)
src/ui/pages/apiKeys/ManageKeys.page.tsx (1)
19-19: LGTM!The container size adjustment aligns with the card-based user rendering introduced in related components.
src/ui/pages/membershipLists/ExternalMemberListManagement.tsx (1)
26-26: LGTM!The integration of
NameOptionalUserCardfor member display is consistent with the PR's batch name resolution feature. The column header change from "Member NetID" to "Member" appropriately reflects the card-based rendering.Also applies to: 331-331, 378-378
src/ui/pages/iam/GroupMemberManagement.tsx (1)
25-25: LGTM!The replacement of inline avatar/name/email rendering with
NameOptionalUserCardprovides a consistent user display pattern across the application.Also applies to: 192-192
src/ui/pages/iam/GroupMemberManagement.test.tsx (1)
8-8: LGTM!Wrapping the component with
UserResolverProvider(withresolutionDisabled) ensures the test environment provides the necessary context forNameOptionalUserCardwithout triggering API calls.Also applies to: 23-28
src/ui/pages/apiKeys/ManageKeysTable.tsx (1)
41-41: LGTM!The use of
NameOptionalUserCardwithsize="sm"in the table context is appropriate and standardizes owner display across the application.Also applies to: 212-212
src/ui/App.tsx (1)
29-31: LGTM!Wrapping the
RouterwithUserResolverProviderappropriately establishes the user resolution context for all child components that useNameOptionalUserCard.src/ui/pages/stripe/CurrentLinks.test.tsx (1)
8-8: LGTM!The test infrastructure correctly provides
UserResolverProvidercontext withresolutionDisabled, aligning with the new standardized user rendering approach.Also applies to: 31-36
src/ui/pages/stripe/CurrentLinks.tsx (1)
21-21: LGTM!The replacement of conditional "You" display logic with
NameOptionalUserCardstandardizes user rendering and removes the dependency onuseAuthfor display purposes.Also applies to: 187-187
src/ui/pages/apiKeys/ManageKeysTable.test.tsx (1)
78-93: Good use ofUserResolverProviderin the test harnessWrapping
OrgApiKeyTablewithUserResolverProvider(withresolutionDisabled) is a clean way to keep tests independent of network-based name resolution while still exercising the new owner rendering path.src/ui/pages/logs/LogRenderer.test.tsx (1)
40-51: User resolver provider wrapping looks appropriateWrapping
LogRendererwithUserResolverProvider(usingresolutionDisabledin tests) aligns the test harness with the new NameOptionalUserCard-based rendering without introducing network dependencies.src/ui/pages/logs/LogRenderer.tsx (1)
213-245: Actor/target rendering changes align well with the new user resolverThe new use of
NameOptionalUserCardfor theactorandtargetcolumns looks consistent with the batch name-resolution design:
actornow benefits from name resolution while still taking a plain string.targetcleanly distinguishes between module-valued targets (AUDIT_LOG +Modules.*) and user/email targets, with a fallback that preserves the original email/text.No functional issues spotted here; just ensure the parent tree always includes
UserResolverProvider(as your updated tests already do).src/api/functions/uin.ts (1)
242-317: Robust DynamoDB batch get implementation with sensible retry behavior
batchGetUserInfolooks solid:
- Respects DynamoDB’s 100-item BatchGetItem limit via
BATCH_SIZE.- Uses
ProjectionExpressionto only retrieveid,firstName,lastName, and safely builds a mapping from email →{ firstName?, lastName? }.- Reuses
retryDynamoTransactionWithBackoff, triggering retries whenUnprocessedKeysis present and logging a warning while returning partial results if the batch still fails.I don’t see correctness issues here given the surrounding utilities; the function should integrate cleanly with the new batchResolveInfo route.
src/api/routes/user.ts (2)
12-21: Check import path style vs ESLintimport/extensionsexpectationsThe new imports from
common/types/user.jsandapi/functions/uin.jsfollow the same.js-suffixed pattern as the rest of this file, but the static analysis hints are flagging them as violations ofimport/extensions.If your TS/ESM setup prefers extension-less imports here (e.g.
common/types/user/api/functions/uin), it may be worth aligning these two new imports with that convention to keep lint green. Otherwise, consider adjusting the ESLint config for this backend package so it matches your intended module resolution rules.
67-98: Confirm authorization level for/batchResolveInfoThe new
/batchResolveInforoute mirrors the existing/findUserByUinshape, but it’s registered withwithRoles([]), whereas/findUserByUinrequiresAppRoles.VIEW_USER_INFO.Since this endpoint resolves emails to user info (names) in batch, please double‑check that exposing it to “any authenticated caller allowed by
authorizeFromSchema” is intentional, and that no additional role gating is needed (e.g. reusingVIEW_USER_INFOor a similar permission).src/ui/components/NameOptionalCard/index.tsx (1)
10-13: Verify local import path vsimport/extensionslint ruleESLint is flagging the
../AuthContextimport as missing a file extension. Other UI files may be using extension‑less local imports as well, so this could be a resolver configuration issue rather than a code issue.Please verify whether your front‑end lint rules expect
.tsx/.jshere (e.g.../AuthContext/index) and adjust either the import path or the ESLintimport/extensionsconfig so this file is consistent with the rest of the UI.
|
|
||
| import ColorSchemeContext from "./ColorSchemeContext"; | ||
| import { Router } from "./Router"; | ||
| import { UserResolverProvider } from "./components/NameOptionalCard"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the ESLint import/extensions violation.
The import statement is missing a file extension. While this may work with your current build configuration, it violates the ESLint rule and could cause issues in some environments.
Based on coding guidelines.
Apply this diff to add the file extension:
-import { UserResolverProvider } from "./components/NameOptionalCard";
+import { UserResolverProvider } from "./components/NameOptionalCard/index";Alternatively, if there's an index.ts or index.tsx file that re-exports from the module, you could be more explicit:
-import { UserResolverProvider } from "./components/NameOptionalCard";
+import { UserResolverProvider } from "./components/NameOptionalCard/index.tsx";Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 ESLint
[error] 11-11: Missing file extension for "./components/NameOptionalCard"
(import/extensions)
🤖 Prompt for AI Agents
In src/ui/App.tsx around line 11, the import for UserResolverProvider lacks a
file extension which violates the ESLint import/extensions rule; update the
import to include the explicit file extension (e.g. import from
"./components/NameOptionalCard.tsx") or, if you intend to import from a barrel
index, point to that file explicitly (e.g.
"./components/NameOptionalCard/index.ts" or ".tsx") so the resolver and linter
both accept it.
| await screen.findByText("User created"); | ||
| expect(screen.getByText("admin")).toBeInTheDocument(); | ||
| expect(screen.getByText("user@example.com")).toBeInTheDocument(); | ||
| expect(screen.getAllByText("user@example.com")).length(2); | ||
| expect(screen.getByText("req-123")).toBeInTheDocument(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct the array length assertion for target email
Same issue as in the API keys tests: expect(screen.getAllByText("user@example.com")).length(2); is invalid and will throw.
Suggested fix:
- expect(screen.getAllByText("user@example.com")).length(2);
+ expect(screen.getAllByText("user@example.com")).toHaveLength(2);🤖 Prompt for AI Agents
In src/ui/pages/logs/LogRenderer.test.tsx around lines 115 to 118, the assertion
expect(screen.getAllByText("user@example.com")).length(2); is invalid and will
throw; replace it with a proper Jest assertion such as
expect(screen.getAllByText("user@example.com")).toHaveLength(2) (or
expect(screen.getAllByText("user@example.com").length).toBe(2)) so the test
correctly asserts there are two matching elements.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/ui/pages/tickets/ViewTickets.page.tsx (1)
301-314: Async clipboard handling is now correct.The
copyTicketIdfunction properly handles the asyncnavigator.clipboard.writeTextcall by making the functionasyncandawaiting the operation. Error handling will now correctly catch permission or other async failures.This addresses the previous review comment about synchronous try/catch not catching Promise rejections.
Based on learnings
tests/live/user.test.ts (1)
1-52: Address remaining test structure issues (Authorization header is now fixed).The Authorization header has been correctly added (lines 19, 36), but three issues from the previous review remain:
Async
describecallback (line 7)
describeblocks should be synchronous; moveconst token = await createJwt()to abeforeAllhookDuplicate imports (lines 2-3)
- Both
createJwtandgetBaseEndpointare imported from./utils.jsin separate statementsWrong response type (line 44)
- Typed as
string[]but used asRecord<string, { firstName?: string; lastName?: string }>Apply this refactor:
-import { expect, test, describe } from "vitest"; -import { createJwt } from "./utils.js"; -import { getBaseEndpoint } from "./utils.js"; +import { expect, test, describe, beforeAll } from "vitest"; +import { createJwt, getBaseEndpoint } from "./utils.js"; const baseEndpoint = getBaseEndpoint(); -describe("User info live tests", async () => { - const token = await createJwt(); +describe("User info live tests", () => { + let token: string; + + beforeAll(async () => { + token = await createJwt(); + }); test( "Test that invalid emails are rejected", { timeout: 10000 }, async () => { const response = await fetch( `${baseEndpoint}/api/v1/users/batchResolveInfo`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emails: ["invalid"], }), }, ); expect(response.status).toBe(400); }, ); test("Test that valid emails are resolved", { timeout: 10000 }, async () => { const response = await fetch( `${baseEndpoint}/api/v1/users/batchResolveInfo`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emails: ["testinguser@illinois.edu"], }), }, ); expect(response.status).toBe(200); - const responseJson = (await response.json()) as string[]; + const responseJson = (await response.json()) as Record< + string, + { firstName?: string; lastName?: string } + >; expect(responseJson).toEqual({ "testinguser@illinois.edu": { firstName: "Testing", lastName: "User", }, }); }); });Based on learnings
🧹 Nitpick comments (3)
tests/e2e/auditLogs.spec.ts (1)
23-23: Timing guard for async name resolution looks reasonable.The 500ms delay accommodates the batched user resolution introduced by
NameOptionalUserCard. This is a pragmatic fix for e2e test stability.For better resilience under load, you could wait for a specific UI state instead of a fixed timeout:
-await page.waitForTimeout(500); +// Wait for skeleton loaders to disappear (indicating resolution complete) +await page.waitForSelector('[data-testid="user-skeleton"]', { state: 'detached', timeout: 2000 }).catch(() => {});However, the fixed delay is acceptable for e2e tests.
src/ui/components/NameOptionalCard/index.tsx (1)
223-241: Past review about polling is no longer applicable; current implementation uses cacheVersion reactivity.The previous review comment about infinite polling with
setIntervalis outdated. The current implementation usescacheVersionas a dependency (line 238) to reactively update when the cache changes, which is a cleaner approach.One performance consideration:
cacheVersionincrements on every cache update (lines 100, 110, 120, 134), causing all mountedNameOptionalUserCardinstances to re-run their effects even when an unrelated email resolves. For pages with many cards, this creates unnecessary work.A more granular approach would be to track per-email resolution status, but the current implementation is functionally correct and acceptable for typical use cases.
Based on learnings
src/ui/pages/tickets/ViewTickets.page.tsx (1)
363-379: Click-to-copy UX is clear and functional.The tooltip "Click to copy ticket ID" provides clear affordance for the interaction. The implementation correctly calls
copyTicketIdon click.One minor UX consideration: clicking the user card (which shows email/name) to copy the ticket ID might be slightly unintuitive. An alternative would be to add a small copy icon button:
<Table.Td> <Group gap="sm" wrap="nowrap"> <NameOptionalUserCard email={ticket.purchaserData.email} size="sm" /> <Tooltip label="Copy ticket ID" position="top" withArrow> <ActionIcon onClick={() => copyTicketId(ticket.ticketId)}> <IconCopy size={16} /> </ActionIcon> </Tooltip> </Group> </Table.Td>However, the current approach is acceptable with the clear tooltip.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (5)
src/ui/components/NameOptionalCard/index.tsx(1 hunks)src/ui/pages/tickets/ViewTickets.page.tsx(4 hunks)tests/e2e/auditLogs.spec.ts(1 hunks)tests/e2e/membershipLists.spec.ts(2 hunks)tests/live/user.test.ts(1 hunks)
🧰 Additional context used
🪛 ESLint
tests/live/user.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/mhvZRjVCNg'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 2-2: Unexpected use of file extension "js" for "./utils.js"
(import/extensions)
[error] 3-3: './utils.js' import is duplicated.
(no-duplicate-imports)
[error] 3-3: Unexpected use of file extension "js" for "./utils.js"
(import/extensions)
src/ui/components/NameOptionalCard/index.tsx
[error] 1-1: Resolve error: EACCES: permission denied, open '/YGsqPelKDq'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 12-12: Missing file extension for "../AuthContext"
(import/extensions)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Unit Tests
🔇 Additional comments (2)
tests/e2e/membershipLists.spec.ts (2)
40-40: Count-based assertions align with NameOptionalUserCard structure.The updated assertions expect exactly 2 occurrences of each NetID, which aligns with the
NameOptionalUserCardrendering both the display name and email address as separate text elements.Also applies to: 48-48
57-59: More specific row selector improves test reliability.The updated selector uses the full rendered display from
NameOptionalUserCard, making the test less likely to match unintended rows.
Summary by CodeRabbit
New Features
Tests