✨ server: add wallet provisioning endpoint#949
Conversation
🦋 Changeset detectedLatest commit: 8e35a6d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
7de9bc2 to
1d0aa0f
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a new authenticated GET /wallet endpoint that loads a credential's ACTIVE|FROZEN card, calls Panda for processor details, returns processorCardId/timeBasedSecret, adds a Panda utility, tests covering success and error mappings, and adds a changeset for a patch release. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant API as GET /wallet
participant DB as Database
participant Panda as Panda API
Client->>API: GET /wallet (credentialId cookie)
API->>DB: Load credential (pandaId, first ACTIVE|FROZEN card)
alt no credential
DB-->>API: not found
API-->>Client: 500 { code: "no credential" }
else credential missing pandaId
DB-->>API: credential (no pandaId)
API-->>Client: 403 { code: "no panda" }
else no eligible card
DB-->>API: credential (no eligible card)
API-->>Client: 404 { code: "no card" }
else credential + card found
DB-->>API: credential + cardId
API->>Panda: GET /issuing/cards/{cardId}/processorDetails
alt processor details found
Panda-->>API: { processorCardId, timeBasedSecret }
API-->>Client: 200 { cardId, cardSecret }
else processor 404
Panda-->>API: 404
API-->>Client: 404 { code: "no card" }
else other error
Panda-->>API: error
API-->>Client: 500 (error)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
|
✅ All tests passed. |
af7c987 to
9495bf1
Compare
9495bf1 to
0975d4d
Compare
0975d4d to
0ed88f7
Compare
0ed88f7 to
ba4376c
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
server/test/api/card.test.ts (1)
867-869:⚠️ Potential issue | 🟡 MinorUse
Pandain the mockedServiceErrorfor consistency.
"Rain"works, but using the real provider label keeps test fixtures closer to production behavior.Suggested fix
- vi.spyOn(panda, "getProcessorDetails").mockRejectedValueOnce(new ServiceError("Rain", 500, "internal error")); + vi.spyOn(panda, "getProcessorDetails").mockRejectedValueOnce(new ServiceError("Panda", 500, "internal error"));
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7e528c82-bb28-42c9-8cd9-b0f075ed5b87
📒 Files selected for processing (4)
.changeset/chilly-suns-dress.mdserver/api/card.tsserver/test/api/card.test.tsserver/utils/panda.ts
ba4376c to
2f6644b
Compare
9662eac to
b49e535
Compare
b49e535 to
1cf381c
Compare
e5b5193 to
2d5b10a
Compare
2d5b10a to
27851ed
Compare
e304a18 to
231dc19
Compare
231dc19 to
e9ee96c
Compare
e9ee96c to
f093130
Compare
f093130 to
d5d7aa9
Compare
d5d7aa9 to
7a89e5d
Compare
7a89e5d to
d7fcd10
Compare
| it("propagates stale card provisioning errors when user lookup fails", async () => { | ||
| const stale = new ServiceError("Panda", 404, "not found"); | ||
| const forbidden = new ServiceError( | ||
| "Panda", | ||
| 403, | ||
| '{"message":"User exists but is not approved yet","error":"ForbiddenError","statusCode":403}', | ||
| "ForbiddenError", | ||
| "User exists but is not approved yet", | ||
| ); | ||
| vi.spyOn(panda, "getSecrets").mockResolvedValueOnce(panTemplate); | ||
| vi.spyOn(panda, "getPIN").mockResolvedValueOnce(pinTemplate); | ||
| vi.spyOn(panda, "getCard").mockResolvedValueOnce(cardTemplate); | ||
| vi.spyOn(panda, "getUser").mockRejectedValueOnce(forbidden); | ||
| vi.spyOn(panda, "getProcessorDetails").mockRejectedValueOnce(stale); | ||
|
|
||
| const response = await appClient.index.$get( | ||
| { header: { sessionid: "fakeSession" }, query: { scope: "provisioning" } }, | ||
| { headers: { "test-credential-id": "default" } }, | ||
| ); | ||
|
|
||
| expect(response.status).toBe(500); | ||
| }); |
There was a problem hiding this comment.
🚩 Test for concurrent getUser + getProcessorDetails failures has non-deterministic captureException behavior
In the test "propagates stale card provisioning errors when user lookup fails" (server/test/api/card.test.ts:1552-1573), both getUser and getProcessorDetails are mocked to reject. The getUser catch handler at server/api/card.ts:312-337 catches its ForbiddenError and calls captureException (because shouldCapture evaluates to true since card status is "ACTIVE"), then returns null. Meanwhile getProcessorDetails rejects, causing Promise.all to reject. The test only asserts response.status === 500 but does not assert on captureException. Per the server rules' "assert aggressively" mandate, this is a gap — the captureException call from the getUser handler is observable side-effect behavior that should be verified.
Was this helpful? React with 👍 or 👎 to provide feedback.
d7fcd10 to
8e35a6d
Compare
Closes #440
Summary by CodeRabbit
New Features
Tests
Chores