Skip to content

Feat/#11#20

Merged
codebestia merged 9 commits into
ShadeProtocol:mainfrom
EmmanuelAR:feat/#11
Jun 29, 2026
Merged

Feat/#11#20
codebestia merged 9 commits into
ShadeProtocol:mainfrom
EmmanuelAR:feat/#11

Conversation

@EmmanuelAR

@EmmanuelAR EmmanuelAR commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Closes #11
Evidence: https://www.loom.com/share/92e6a0e28cf34c4fa02c902f0f2a5a66
Postman collection:
Shade - Merchant Profile API (#11).postman_collection.json

Summary by CodeRabbit

  • New Features

    • Added authenticated merchant “My Profile” endpoints: GET /me and PATCH /me.
    • Expanded merchant profile responses with additional public details (including webhook and account).
  • Bug Fixes

    • Improved partial profile updates: trims text, ignores non-editable fields, and supports clearing optional values (e.g., logo/webhook set to null).
    • Strengthened update validation with consistent 400 responses and HTTPS-only webhook enforcement.
  • Tests

    • Added integration and unit coverage for profile retrieval/updates and validation behavior.
    • Updated JWT secret assertion to use runtime environment configuration.

- Added `getMyProfileController` and `updateMyProfileController` to handle fetching and updating the authenticated merchant's profile.
- Introduced validation for updating merchant profiles with `validateUpdateMerchant`.
- Updated routes to include new endpoints for profile management.
- Created integration and unit tests for the new profile functionalities, ensuring proper authentication and validation handling.
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds authenticated GET /merchants/me and PATCH /merchants/me endpoints with update validation, sanitized profile reads, partial profile updates, and unit/integration test coverage. Also updates one access-token test to use the configured JWT secret.

Changes

Merchant Profile API

Layer / File(s) Summary
Update validation contract
src/utils/validation.ts, tests/unit/merchant.update.validation.test.ts
Defines UpdateMerchantInput, editable field sets, HTTPS URL validation, and update payload checks for empty, text, logo, and webhook inputs. Tests cover valid partial updates, empty payloads, clearable fields, and webhook/logo validation.
Merchant profile services
src/services/merchant.services.ts, tests/unit/merchant.profile.services.test.ts
Expands sanitizeMerchant to include additional profile fields, adds getMyProfile and updateMyProfile, and tests lookup, 404 handling, trimming, null normalization, and sanitized return values.
Controllers and route wiring
src/controllers/merchant.controllers.ts, src/routes/merchant.routes.ts, tests/integration/merchant.profile.test.ts
Adds authenticated profile controllers, registers GET /me and PATCH /me, and tests unauthenticated access, profile reads, partial updates, ignored non-editable fields, and validation failures.

Auth Token Test Update

Layer / File(s) Summary
JWT secret fixture
tests/unit/auth.services.test.ts
Uses the configured JWT secret in the access-token verification test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • codebestia

Poem

A bunny hopped to the merchant gate,
Fetching a profile, neat and great.
/me said hello,
PATCH made it glow,
With webhook hops and fields update. 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The auth.services test change is unrelated to the merchant profile API objectives and appears to be an extra CI-oriented tweak. Remove unrelated auth test changes from this PR or move them to a separate fix.
Title check ❓ Inconclusive The title is too vague and doesn't describe the merchant profile API changes. Use a concise, descriptive title like 'Add merchant profile GET/PATCH endpoints'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Adds protected /me GET and PATCH routes, validates updates, ignores forbidden fields, and hides internal data as required.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

- Deleted the implementation plan and design spec for the Merchant Profile API as they are no longer relevant to the current project structure.

@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.

🧹 Nitpick comments (5)
tests/integration/merchant.profile.test.ts (2)

56-59: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Assert the sensitive fields named in the API contract.

These checks cover relation leakage, but the linked issue specifically forbids exposing emailOtp and API-key hash fields. Adding assertions for those fields will keep the sanitizer contract from regressing silently.

🤖 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 `@tests/integration/merchant.profile.test.ts` around lines 56 - 59, The
integration test for the merchant profile response currently checks for some
hidden fields but misses the contract-specific sensitive fields. Update the
assertions in merchant.profile.test.ts around the response checks to also verify
that response.body does not expose emailOtp or API-key hash fields, alongside
the existing refreshTokens/apiKeys checks, so the sanitizer contract is covered
by the test.

85-99: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover all explicitly non-editable fields in the ignore-path test.

This only exercises address and email, but merchantId and account are also part of the immutable contract. Including them here would better protect against accidental writes to identity/account fields.

Suggested test expansion
     const response = await request(app)
       .patch(ME_URL)
       .set('Authorization', 'Bearer valid-token')
-      .send({ firstName: 'Grace', address: '0xHACK', email: 'evil@example.com' });
+      .send({
+        firstName: 'Grace',
+        address: '0xHACK',
+        email: 'evil@example.com',
+        merchantId: 999,
+        account: '0xHACKED',
+      });
 
     expect(response.status).toBe(200);
     const updateArg = prismaMock.merchant.update.mock.calls[0][0];
     expect(updateArg.data).toEqual({ firstName: 'Grace' });
     expect(response.body.address).toBe('0x123');
     expect(response.body.email).toBe('ada@example.com');
+    expect(response.body.merchantId).toBe(1);
+    expect(response.body.account).toBe('CCONTRACT');
🤖 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 `@tests/integration/merchant.profile.test.ts` around lines 85 - 99, Expand the
ignore-path test in merchant.profile.test.ts so it also covers the other
immutable fields enforced by the merchant profile update flow. In the existing
silently ignores non-editable fields test around the PATCH to ME_URL, include
merchantId and account in the sent payload alongside address and email, and keep
asserting that prismaMock.merchant.update only receives editable data (via the
updateArg.data check) while the response still preserves the original
merchantId/account values. Use the existing merchant.profile test setup and
prismaMock.merchant.update mock to verify these fields are stripped before
persistence.
tests/unit/merchant.update.validation.test.ts (1)

23-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add logo: '' coverage for clear semantics.

The contract gives logo the same clear-to-null behavior as webhook, but this suite only locks in the webhook branch. A regression in validateUpdateMerchant({ logo: '' }) would currently slip through.

Suggested test
   test('accepts logo and webhook cleared with null', () => {
     expect(validateUpdateMerchant({ logo: null, webhook: null })).toEqual({});
   });
 
+  test('accepts an empty string logo as a clear', () => {
+    expect(validateUpdateMerchant({ logo: '' })).toEqual({});
+  });
+
   test('accepts an empty string webhook as a clear', () => {
     expect(validateUpdateMerchant({ webhook: '' })).toEqual({});
   });
🤖 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 `@tests/unit/merchant.update.validation.test.ts` around lines 23 - 29, Add
coverage for the clear-to-null behavior of validateUpdateMerchant by extending
the existing validation tests in merchant.update.validation.test.ts to assert
that an empty string for logo is treated the same as webhook. Update or add a
test near the current validateUpdateMerchant cases so that
validateUpdateMerchant({ logo: '' }) returns an empty object, matching the clear
semantics already covered for webhook.
tests/unit/merchant.profile.services.test.ts (1)

7-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Sanitization assertions are vacuous because baseMerchant omits the sensitive fields.

baseMerchant has no refreshTokens/apiKeys, so expect(result).not.toHaveProperty('refreshTokens'/'apiKeys') (Lines 38-39, 83) passes trivially and does not actually verify the allowlist drops internal fields. Add the sensitive fields to the fixture so the assertions exercise the sanitizer.

🧪 Strengthen the fixture
   createdAt: new Date(),
   updatedAt: new Date(),
+  // internal fields that must be stripped by sanitizeMerchant
+  refreshTokens: [{ token: 'secret' }],
+  apiKeys: [{ hash: 'hashed' }],
+  emailOtp: '123456',
 };
🤖 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 `@tests/unit/merchant.profile.services.test.ts` around lines 7 - 26, The
sanitizer tests are currently vacuous because the shared `baseMerchant` fixture
in `merchant.profile.services.test.ts` does not include sensitive fields, so the
`sanitizeMerchantProfile` assertions never prove they are removed. Update
`baseMerchant` to include internal properties like `refreshTokens` and
`apiKeys`, then keep the existing `expect(result).not.toHaveProperty(...)`
checks so they verify the allowlist behavior of `sanitizeMerchantProfile` and
the related service output.
src/controllers/merchant.controllers.ts (1)

66-110: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Both controllers correctly mirror the established auth/validation/error pattern. 401 guard, validateUpdateMerchant 400 path, and AppError→status / generic→500 mapping are consistent with registerMerchantController.

Optional: the try/catch + AppError/500 block is now duplicated across three controllers; extracting an asyncHandler/error-mapping wrapper would remove the repetition. Deferable.

🤖 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 `@src/controllers/merchant.controllers.ts` around lines 66 - 110, The
controllers already follow the expected auth, validation, and AppError/500
mapping pattern, so no functional change is needed in getMyProfileController or
updateMyProfileController. If you want to address the duplication noted in the
review, extract the repeated try/catch error mapping into a reusable async
handler or shared helper and have getMyProfileController,
updateMyProfileController, and registerMerchantController use it consistently.
🤖 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.

Nitpick comments:
In `@src/controllers/merchant.controllers.ts`:
- Around line 66-110: The controllers already follow the expected auth,
validation, and AppError/500 mapping pattern, so no functional change is needed
in getMyProfileController or updateMyProfileController. If you want to address
the duplication noted in the review, extract the repeated try/catch error
mapping into a reusable async handler or shared helper and have
getMyProfileController, updateMyProfileController, and
registerMerchantController use it consistently.

In `@tests/integration/merchant.profile.test.ts`:
- Around line 56-59: The integration test for the merchant profile response
currently checks for some hidden fields but misses the contract-specific
sensitive fields. Update the assertions in merchant.profile.test.ts around the
response checks to also verify that response.body does not expose emailOtp or
API-key hash fields, alongside the existing refreshTokens/apiKeys checks, so the
sanitizer contract is covered by the test.
- Around line 85-99: Expand the ignore-path test in merchant.profile.test.ts so
it also covers the other immutable fields enforced by the merchant profile
update flow. In the existing silently ignores non-editable fields test around
the PATCH to ME_URL, include merchantId and account in the sent payload
alongside address and email, and keep asserting that prismaMock.merchant.update
only receives editable data (via the updateArg.data check) while the response
still preserves the original merchantId/account values. Use the existing
merchant.profile test setup and prismaMock.merchant.update mock to verify these
fields are stripped before persistence.

In `@tests/unit/merchant.profile.services.test.ts`:
- Around line 7-26: The sanitizer tests are currently vacuous because the shared
`baseMerchant` fixture in `merchant.profile.services.test.ts` does not include
sensitive fields, so the `sanitizeMerchantProfile` assertions never prove they
are removed. Update `baseMerchant` to include internal properties like
`refreshTokens` and `apiKeys`, then keep the existing
`expect(result).not.toHaveProperty(...)` checks so they verify the allowlist
behavior of `sanitizeMerchantProfile` and the related service output.

In `@tests/unit/merchant.update.validation.test.ts`:
- Around line 23-29: Add coverage for the clear-to-null behavior of
validateUpdateMerchant by extending the existing validation tests in
merchant.update.validation.test.ts to assert that an empty string for logo is
treated the same as webhook. Update or add a test near the current
validateUpdateMerchant cases so that validateUpdateMerchant({ logo: '' })
returns an empty object, matching the clear semantics already covered for
webhook.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 986e1cb3-78bf-447b-b05c-a19f26e03f37

📥 Commits

Reviewing files that changed from the base of the PR and between c0e30a5 and a3c95f6.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • docs/superpowers/plans/2026-06-26-merchant-profile-api.md
  • docs/superpowers/specs/2026-06-26-merchant-profile-api-design.md
  • src/controllers/merchant.controllers.ts
  • src/routes/merchant.routes.ts
  • src/services/merchant.services.ts
  • src/utils/validation.ts
  • tests/integration/merchant.profile.test.ts
  • tests/unit/merchant.profile.services.test.ts
  • tests/unit/merchant.update.validation.test.ts

…ations

- Updated integration and unit tests for merchant profile to include internal relations like refreshTokens and apiKeys.
- Modified the test for non-editable fields to account for additional fields: merchantId and account.
- Added validation for accepting empty string values for logo and webhook in update validation tests.
@codebestia

Copy link
Copy Markdown
Contributor

@EmmanuelAR
Great job so far.

Please fix the CI.

EmmanuelAR and others added 4 commits June 28, 2026 17:51
…nt variable for secret

- Imported environment configuration to access the JWT secret.
- Modified the JWT verification in tests to utilize the environment variable instead of a hardcoded value.
@codebestia codebestia merged commit 483bfab into ShadeProtocol:main Jun 29, 2026
3 checks passed
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.

Merchant Profile API (Get & Update)

2 participants