Skip to content

Conversation

@devksingh4
Copy link
Member

@devksingh4 devksingh4 commented Nov 5, 2025

Summary by CodeRabbit

  • New Features

    • Retrieve user purchase history for tickets and merchandise via a new purchases endpoint.
    • UIN-to-email lookup endpoint to find users by UIN.
    • Enhanced ticket scanner UI: manual NetID/Email entry and ticket-selection modal for multiple matches.
  • Changes

    • API ticket routes moved to an /event/ path.
    • Purchase records now show refunded and fulfilled status.
  • Tests

    • Added unit and live tests covering purchase retrieval and new endpoints.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 5, 2025

Walkthrough

Adds DynamoDB-backed user purchase fetchers and a GET /purchases/:email API, introduces POST /users/findUserByUin, updates ticket routes to use /event/:eventId, refactors ScanTicketsPage for manual UIN/email lookup and multi-ticket selection, and adds tests and Terraform/front-end endpoint adjustments.

Changes

Cohort / File(s) Change Summary
Purchase Retrieval Functions
src/api/functions/tickets.ts
Adds getUserTicketingPurchases and getUserMerchPurchases, GetUserPurchasesInputs, RawTicketEntry, RawMerchEntry; DynamoDB QueryCommands against UserIndex, unmarshalling, mapping to TicketInfoEntry, and DatabaseFetchError handling.
Ticket API Routes
src/api/routes/tickets.ts
Extends ticketInfoEntryZod with refunded and fulfilled, exports TicketInfoEntry, removes legacy response schemas, changes route paths from /:eventId to /event/:eventId, adds GET /purchases/:email that concurrently returns { merch, tickets } using new fetchers.
User API Route & Types
src/api/routes/user.ts, src/common/types/user.ts
New userRoute module with POST /users/findUserByUin; hashes UIN, queries DynamoDB UinHashIndex, validates results, and returns { email }. Adds Zod schemas searchUserByUinRequest and searchUserByUinResponse.
API Registration
src/api/index.ts
Imports and registers userRoute under /users at both root and within /api/v1.
Scan Tickets UI
src/ui/pages/tickets/ScanTickets.page.tsx
Large refactor: adds ScanTicketsPageProps, new data shapes (PurchasesByEmailResponse, TicketItem, etc.), UIN-to-email flow via new user endpoint, manual email/UIN entry, purchase lookup, grouped item selection, modal for multi-ticket selection, check-in flow integration, and refunded/fulfilled handling.
Ticket UI Endpoint Updates
src/ui/pages/tickets/SelectEventId.page.tsx, src/ui/pages/tickets/ViewTickets.page.tsx
Update API calls to use /api/v1/tickets/event/{eventId} instead of /api/v1/tickets/{eventId}; behavior otherwise unchanged.
Unit Tests (functions)
tests/unit/functions/tickets.test.ts
Adds comprehensive tests for ticket and merch fetchers: empty results, transformations, precise Query input assertions, error paths (undefined items, query failures, BaseError rethrow), and merch-specific fields.
Unit & Live Tests (routes)
tests/unit/tickets.test.ts, tests/live/tickets.test.ts
Update test endpoints to /api/v1/tickets/event/...; add tests for GET /purchases/:email covering success and error scenarios; live test validates { merch, tickets } arrays.
Terraform Frontend
terraform/modules/frontend/main.tf
Adds CloudFront ordered_cache_behavior for /api/v1/users/findUserByUin mirroring existing API behaviors.
Terraform Lambdas
terraform/modules/lambdas/main.tf
Expands DynamoDB resource ARN in IAM policy to include an index path for infra-events-tickets table.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as GET /purchases/:email
    participant Tickets as getUserTicketingPurchases
    participant Merch as getUserMerchPurchases
    participant DynamoDB

    Client->>API: Fetch user purchases
    
    rect rgb(200, 220, 255)
    Note over API: Concurrent fetch
    par
        API->>Tickets: Call with email
        Tickets->>DynamoDB: QueryCommand (TicketPurchasesTableName, UserIndex)
        DynamoDB-->>Tickets: Items[]
        Tickets->>Tickets: Transform to TicketInfoEntry[]
        Tickets-->>API: tickets[]
    and
        API->>Merch: Call with email
        Merch->>DynamoDB: QueryCommand (MerchStorePurchasesTableName, UserIndex)
        DynamoDB-->>Merch: Items[]
        Merch->>Merch: Transform to TicketInfoEntry[]
        Merch-->>API: merch[]
    end
    end
    
    alt Success
        API->>Client: 200 { merch, tickets }
    else Error
        API->>Client: DatabaseFetchError
    end
Loading
sequenceDiagram
    participant User
    participant ScanTickets as ScanTicketsPage
    participant API
    participant UinAPI as POST /users/findUserByUin
    participant DynamoDB

    User->>ScanTickets: Enter UIN or Email
    alt UIN Entry
        ScanTickets->>ScanTickets: Validate 9 digits
        ScanTickets->>UinAPI: Hash UIN & search
        UinAPI->>DynamoDB: Query UinHashIndex
        DynamoDB-->>UinAPI: User item
        UinAPI-->>ScanTickets: email
        ScanTickets->>ScanTickets: Set email
    else Email Entry
        ScanTickets->>ScanTickets: Validate email format
        ScanTickets->>ScanTickets: Set email
    end
    
    ScanTickets->>API: GET /purchases/:email
    API-->>ScanTickets: { tickets[], merch[] }
    
    alt Multiple valid items
        ScanTickets->>User: Show item selection modal
        User->>ScanTickets: Select ticket/merch item
    else Single item
        ScanTickets->>ScanTickets: Auto-select
    end
    
    User->>ScanTickets: Initiate check-in (camera/manual)
    ScanTickets->>API: POST checkInTicket
    API-->>ScanTickets: Success
    ScanTickets->>User: Show confirmation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay attention to:
    • DynamoDB query inputs, marshalling/unmarshalling, and transformation correctness in src/api/functions/tickets.ts.
    • Error handling differences (rethrow vs wrapping) and logging behavior in fetchers and route handlers.
    • Large UI refactor in ScanTickets.page.tsx: props, state flows, modal logic, and integration points for check-in and purchase lookup.
    • Security/validation in POST /users/findUserByUin (UIN hashing and DynamoDB index usage).
    • Route path updates across API and UI to ensure all clients and tests use /event/:eventId.

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective: adding functionality to query and fulfill purchases by email, which aligns with the new /purchases/:email endpoint and supporting functions.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dsingh14/query-purchases-by-email

📜 Recent 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

📥 Commits

Reviewing files that changed from the base of the PR and between b767c7a and 62efaab.

📒 Files selected for processing (2)
  • terraform/modules/frontend/main.tf (1 hunks)
  • terraform/modules/lambdas/main.tf (1 hunks)
⏰ 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 (2)
terraform/modules/lambdas/main.tf (1)

225-225: Appropriate DynamoDB index access expansion.

The addition of the index ARN pattern for infra-events-tickets is consistent with the existing pattern used for other tables (e.g., infra-merchstore-purchase-history on line 228). This permits Query operations against secondary indexes, which is necessary for the email-based purchase lookup feature.

terraform/modules/frontend/main.tf (1)

236-249: CloudFront behavior properly configured and positioned.

The new behavior for /api/v1/users/findUserByUin is correctly configured with the appropriate cache policy, compression, origin key injection, and HTTP methods. The placement ahead of less-specific patterns like /api/v1/events* ensures correct routing precedence.

Please verify that routing this endpoint to SlowLambdaFunction is intentional. The endpoint is positioned with /api/v1/syncIdentity rather than with the regular LambdaFunction endpoints. If user lookup is expected to be lightweight, consider routing to LambdaFunction-${var.CurrentActiveRegion} instead.


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

@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

💰 Infracost report

Monthly estimate generated

This comment will be updated when code changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/routes/tickets.ts (1)

262-273: Always emit boolean defaults for refunded / fulfilled
ticketInfoEntryZod requires these fields to be booleans, but many historical purchases never set either attribute. When unmarshall returns undefined, reply.send drops the property entirely during JSON serialization, so clients get objects that fail your documented schema. Default both fields to false (e.g. Boolean(unmarshalled.refunded)) before sending. (developer.mozilla.org)

           issuedTickets.push({
             type: "merch",
             valid: true,
             ticketId: unmarshalled.stripe_pi,
-            refunded: unmarshalled.refunded,
-            fulfilled: unmarshalled.fulfilled,
+            refunded: Boolean(unmarshalled.refunded),
+            fulfilled: Boolean(unmarshalled.fulfilled),
             purchaserData: {
               email: unmarshalled.email,
               productId: eventId,
📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between 922d017 and 5177602.

📒 Files selected for processing (7)
  • src/api/functions/tickets.ts (1 hunks)
  • src/api/routes/tickets.ts (8 hunks)
  • src/ui/pages/tickets/SelectEventId.page.tsx (1 hunks)
  • src/ui/pages/tickets/ViewTickets.page.tsx (1 hunks)
  • tests/live/tickets.test.ts (1 hunks)
  • tests/unit/functions/tickets.test.ts (1 hunks)
  • tests/unit/tickets.test.ts (3 hunks)
🧰 Additional context used
🪛 ESLint
tests/unit/functions/tickets.test.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/AqbsrpCfmG'
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-5: Replace ⏎··DynamoDBClient,⏎··QueryCommand,⏎ with ·DynamoDBClient,·QueryCommand·

(prettier/prettier)


[error] 6-6: Replace ·getUserMerchPurchases,·getUserTicketingPurchases· with ⏎··getUserMerchPurchases,⏎··getUserTicketingPurchases,⏎

(prettier/prettier)


[error] 6-6: Unexpected use of file extension "js" for "../../../src/api/functions/tickets.js"

(import/extensions)


[error] 7-7: Unexpected use of file extension "js" for "../../../src/common/config.js"

(import/extensions)


[error] 10-10: Unexpected use of file extension "js" for "../../../src/common/errors/index.js"

(import/extensions)


[error] 134-134: Insert ,

(prettier/prettier)


[error] 149-149: Insert ,

(prettier/prettier)


[error] 164-164: Insert ,

(prettier/prettier)


[error] 282-282: Insert ,

(prettier/prettier)


[error] 297-297: Insert ,

(prettier/prettier)


[error] 312-312: Insert ,

(prettier/prettier)

src/api/functions/tickets.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/opILUcQNos'
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] 3-3: Unexpected use of file extension "js" for "api/routes/tickets.js"

(import/extensions)


[error] 4-4: Unexpected use of file extension "js" for "api/types.js"

(import/extensions)


[error] 5-5: Unexpected use of file extension "js" for "common/config.js"

(import/extensions)


[error] 6-6: Unexpected use of file extension "js" for "common/errors/index.js"

(import/extensions)

src/api/routes/tickets.ts

[error] 34-34: Unexpected use of file extension "js" for "api/functions/tickets.js"

(import/extensions)

tests/live/tickets.test.ts

[error] 50-50: ["merch"] is better written in dot notation.

(dot-notation)


[error] 51-51: ["tickets"] is better written in dot notation.

(dot-notation)

⏰ 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

@devksingh4 devksingh4 changed the title Add API route query purchases by email Allow querying and fulfilling purchases by email Nov 5, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
tests/unit/functions/tickets.test.ts (1)

223-233: Assert the real merch quantity returned by the helper

This fixture defines quantity: 2, yet the expectation still forces purchaserData.quantity to 1. That hides the production bug we just flagged and diverges from the contract other tests enforce. Please assert the stored value so the test fails if the helper stops returning the actual quantity.

       purchaserData: {
         email: testEmail,
         productId: "merch-001",
-        quantity: 1,
+        quantity: 2,
       },
src/api/functions/tickets.ts (1)

118-125: Use the actual merch quantity when mapping purchases

RawMerchEntry.quantity already carries what the customer bought, but the transformation clamps it to 1. Any multi-quantity merch order will therefore be under-reported to the API caller. Please forward the stored value (falling back to 1 only if it’s missing) so the endpoint reflects reality.

       purchaserData: {
         email: item.email,
         productId: item.item_id,
-        quantity: 1,
+        quantity:
+          typeof item.quantity === "number" && Number.isFinite(item.quantity)
+            ? item.quantity
+            : 1,
       },
📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5177602 and eb1c8fe.

📒 Files selected for processing (3)
  • src/api/functions/tickets.ts (1 hunks)
  • tests/unit/functions/tickets.test.ts (1 hunks)
  • tests/unit/tickets.test.ts (3 hunks)
🧰 Additional context used
🪛 ESLint
tests/unit/functions/tickets.test.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/xOrGKCtoqu'
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-5: Replace ⏎··DynamoDBClient,⏎··QueryCommand,⏎ with ·DynamoDBClient,·QueryCommand·

(prettier/prettier)


[error] 6-6: Replace ·getUserMerchPurchases,·getUserTicketingPurchases· with ⏎··getUserMerchPurchases,⏎··getUserTicketingPurchases,⏎

(prettier/prettier)


[error] 6-6: Unexpected use of file extension "js" for "../../../src/api/functions/tickets.js"

(import/extensions)


[error] 7-7: Unexpected use of file extension "js" for "../../../src/common/config.js"

(import/extensions)


[error] 10-10: Unexpected use of file extension "js" for "../../../src/common/errors/index.js"

(import/extensions)


[error] 134-134: Insert ,

(prettier/prettier)


[error] 149-149: Insert ,

(prettier/prettier)


[error] 164-164: Insert ,

(prettier/prettier)


[error] 282-282: Insert ,

(prettier/prettier)


[error] 297-297: Insert ,

(prettier/prettier)


[error] 312-312: Insert ,

(prettier/prettier)

src/api/functions/tickets.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/mFcmVmwZVk'
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] 3-3: Unexpected use of file extension "js" for "api/routes/tickets.js"

(import/extensions)


[error] 4-4: Unexpected use of file extension "js" for "api/types.js"

(import/extensions)


[error] 5-5: Unexpected use of file extension "js" for "common/config.js"

(import/extensions)


[error] 6-6: Unexpected use of file extension "js" for "common/errors/index.js"

(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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between eb1c8fe and 82e1a05.

📒 Files selected for processing (3)
  • src/api/functions/tickets.ts (1 hunks)
  • tests/unit/functions/tickets.test.ts (1 hunks)
  • tests/unit/tickets.test.ts (3 hunks)
🧰 Additional context used
🪛 ESLint
src/api/functions/tickets.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/SJDWChrxMv'
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] 3-3: Unexpected use of file extension "js" for "api/routes/tickets.js"

(import/extensions)


[error] 4-4: Unexpected use of file extension "js" for "api/types.js"

(import/extensions)


[error] 5-5: Unexpected use of file extension "js" for "common/config.js"

(import/extensions)


[error] 6-6: Unexpected use of file extension "js" for "common/errors/index.js"

(import/extensions)

tests/unit/functions/tickets.test.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/UAZDjrDOfF'
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-5: Replace ⏎··DynamoDBClient,⏎··QueryCommand,⏎ with ·DynamoDBClient,·QueryCommand·

(prettier/prettier)


[error] 6-6: Replace ·getUserMerchPurchases,·getUserTicketingPurchases· with ⏎··getUserMerchPurchases,⏎··getUserTicketingPurchases,⏎

(prettier/prettier)


[error] 6-6: Unexpected use of file extension "js" for "../../../src/api/functions/tickets.js"

(import/extensions)


[error] 7-7: Unexpected use of file extension "js" for "../../../src/common/config.js"

(import/extensions)


[error] 10-10: Unexpected use of file extension "js" for "../../../src/common/errors/index.js"

(import/extensions)


[error] 134-134: Insert ,

(prettier/prettier)


[error] 149-149: Insert ,

(prettier/prettier)


[error] 164-164: Insert ,

(prettier/prettier)


[error] 282-282: Insert ,

(prettier/prettier)


[error] 297-297: Insert ,

(prettier/prettier)


[error] 312-312: Insert ,

(prettier/prettier)

🔇 Additional comments (5)
tests/unit/tickets.test.ts (3)

29-29: LGTM! Appropriate import for test data preparation.

The marshall import is correctly used in the new test suite to prepare DynamoDB item mocks.


501-501: LGTM! Route paths updated consistently.

The path changes from /:eventId to /event/:eventId are applied consistently across the test suite.

Also applies to: 513-513


521-768: Excellent test coverage! Quantity issue from past review is now fixed.

The new test suite comprehensively covers the user purchases endpoint with proper handling of the stored quantity values. Line 584 correctly expects quantity: 2 matching the mocked merch item at line 546, addressing the previous concern about hardcoded quantities.

Coverage includes:

  • Happy paths: all purchases, no purchases, tickets only, merch only, multiple items
  • Sad paths: invalid email format, database errors on both queries
tests/unit/functions/tickets.test.ts (1)

1-356: LGTM! Comprehensive unit tests with proper quantity handling.

This test file provides excellent coverage of the new purchase retrieval functions. Key strengths:

  • Line 230 correctly expects quantity: 2 from the mocked merch data, properly addressing the past review concern about hardcoded quantities
  • Thorough error handling validation (Items undefined, query failures, BaseError rethrow)
  • Query parameter correctness verification
  • Proper testing of optional fields (scanIsoTimestamp, scannerEmail)

The ESLint formatting errors flagged by static analysis are non-critical and can be auto-fixed by the formatter.

src/api/functions/tickets.ts (1)

117-128: LGTM! Merch quantity now correctly uses stored value.

Line 124 now properly uses item.quantity instead of hardcoding to 1, addressing the issue flagged in past reviews. This ensures multi-quantity merch purchases are accurately reported.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between 82e1a05 and 3837365.

📒 Files selected for processing (2)
  • src/ui/pages/tickets/ScanTickets.page.test.tsx (1 hunks)
  • src/ui/pages/tickets/ScanTickets.page.tsx (10 hunks)
🧰 Additional context used
🪛 ESLint
src/ui/pages/tickets/ScanTickets.page.test.tsx

[error] 1-1: Resolve error: EACCES: permission denied, open '/JYuaAKvgHp'
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)

⏰ 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: Run Unit Tests
  • GitHub Check: Build Application

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/ui/pages/tickets/ScanTickets.page.tsx (3)

179-196: UIN→email lookup is now implemented.

The getEmailFromUINDefault function now properly calls the /api/v1/users/findUserByUin API, addressing the critical issue from the previous review.

However, the ValidationError handling pattern (lines 184-192) is unnecessarily complex.

Consider simplifying the error handling:

-    const samp = new ValidationError({
-      message: "Failed to convert UIN to email.",
-    });
-    if (
-      error.response?.status === samp.httpStatusCode &&
-      error.response?.data.id === samp.id
-    ) {
-      const validationData = error.response.data;
-      throw new ValidationError(validationData.message || samp.message);
-    }
+    if (error.response?.status === 400 && error.response?.data?.name === "ValidationError") {
+      throw new ValidationError(error.response.data.message || "Failed to convert UIN to email.");
+    }

254-271: Consider simplifying the active/inactive logic.

The current logic works but is somewhat convoluted with the double check of itemSalesActive !== false followed by the string type check.

Consider this clearer pattern:

-          const isActive =
-            ticket.itemSalesActive !== false &&
-            (typeof ticket.itemSalesActive === "string"
-              ? new Date(ticket.itemSalesActive) <= now
-              : false);
+          const isActive =
+            typeof ticket.itemSalesActive === "string" &&
+            new Date(ticket.itemSalesActive) <= now;

This simplifies the logic since itemSalesActive is typed as string | false, and only string values represent active items.


607-645: LGTM! Ticket marking logic correctly handles both ticket and merch types.

The function properly constructs the appropriate QR data structure for each product type and handles the check-in flow with proper error handling.

Optional: Extract duplicated error handling.

The error handling pattern (lines 632-637) is duplicated in handleManualInputSubmit, handleSuccessfulScan, and markTicket.

Consider extracting to a helper:

const formatErrorMessage = (err: any): string => {
  if (err.response?.data) {
    return err.response.data
      ? `Error ${err.response.data.id} (${err.response.data.name}): ${err.response.data.message}`
      : "System encountered a failure, please contact the ACM Infra Chairs.";
  }
  return err instanceof Error ? err.message : "Failed to process ticket";
};
src/common/types/user.ts (1)

3-5: Require numeric-only UIN input.

Length-check alone accepts any 9-character string (e.g., letters, hyphenated UINs, whitespace-padded values). Those hash to a value that will never match DynamoDB, so valid lookups fail. Trim and enforce exactly nine digits before hashing.

-export const searchUserByUinRequest = z.object({
-  uin: z.string().length(9)
-});
+export const searchUserByUinRequest = z.object({
+  uin: z
+    .string()
+    .trim()
+    .regex(/^\d{9}$/, "UIN must be exactly nine digits"),
+});
📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3837365 and b767c7a.

📒 Files selected for processing (4)
  • src/api/index.ts (2 hunks)
  • src/api/routes/user.ts (1 hunks)
  • src/common/types/user.ts (1 hunks)
  • src/ui/pages/tickets/ScanTickets.page.tsx (11 hunks)
🧰 Additional context used
🪛 ESLint
src/api/routes/user.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/PydKaXOXCZ'
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 "api/plugins/rateLimiter.js"

(import/extensions)


[error] 3-3: Unexpected use of file extension "js" for "api/components/index.js"

(import/extensions)


[error] 4-4: Unexpected use of file extension "js" for "api/functions/organizations.js"

(import/extensions)


[error] 9-9: Unexpected use of file extension "js" for "common/errors/index.js"

(import/extensions)


[error] 14-14: Unexpected use of file extension "js" for "common/types/user.js"

(import/extensions)


[error] 15-15: Unexpected use of file extension "js" for "api/functions/uin.js"

(import/extensions)


[error] 18-18: Unexpected use of file extension "js" for "common/config.js"

(import/extensions)

src/api/index.ts

[error] 64-64: Unexpected use of file extension "js" for "./routes/user.js"

(import/extensions)

src/common/types/user.ts

[error] 1-1: Resolve error: EACCES: permission denied, open '/hZqryFUXnj'
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] 4-4: Insert ,

(prettier/prettier)

⏰ 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 (4)
src/ui/pages/tickets/ScanTickets.page.tsx (4)

48-60: LGTM! New interfaces support the purchase lookup flow.

The addition of refunded and fulfilled fields to APIResponseSchema and the new PurchasesByEmailResponse interface properly support the manual entry and purchase lookup functionality.


82-103: LGTM! Props interface enables dependency injection for testing.

The ScanTicketsPageProps interface properly defines all injectable dependencies as optional, which allows for both production usage (with defaults) and testing (with mocks).


673-763: LGTM! UI properly implements the manual entry and camera scanning flow.

The conditional rendering based on selectedItemFilter ensures users select an event/item before proceeding. The manual input field with autoFocus and onKeyDown handler provides good UX for both typing and card swiping scenarios.


823-877: LGTM! Ticket selection modal provides clear multi-ticket handling.

The modal properly displays all available tickets with relevant details and allows users to select the correct one when multiple valid tickets exist for the same event.

@devksingh4 devksingh4 merged commit 6b78575 into main Nov 5, 2025
9 of 11 checks passed
@devksingh4 devksingh4 deleted the dsingh14/query-purchases-by-email branch November 5, 2025 04:33
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.

2 participants