Skip to content

feat(auth): uses user aware feature flagging in console web#1494

Merged
ygrishajev merged 1 commit intomainfrom
feature/ff
Jun 16, 2025
Merged

feat(auth): uses user aware feature flagging in console web#1494
ygrishajev merged 1 commit intomainfrom
feature/ff

Conversation

@ygrishajev
Copy link
Contributor

@ygrishajev ygrishajev commented Jun 16, 2025

Summary by CodeRabbit

  • New Features
    • Feature flag evaluations now consider individual user context, enabling more personalized feature rollouts.
  • Refactor
    • Adjusted provider hierarchy to wrap wallet-related providers with feature flag logic, improving context accuracy.
    • Enhanced feature flag services to accept richer context objects, supporting more granular flag checks.
    • Improved server-side feature flag checks by verifying user authentication before evaluation.
  • Bug Fixes
    • Ensured feature flag checks are only performed for authenticated users and return appropriate responses for unauthenticated access.
  • Tests
    • Added and updated tests to verify user context is correctly passed to feature flag providers and services.

@ygrishajev ygrishajev requested a review from a team as a code owner June 16, 2025 12:40
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 16, 2025

Walkthrough

The changes refactor feature flag handling to support richer user context, especially user ID, when evaluating flags. The FlagProvider now injects user context, and provider nesting is adjusted. Feature flag service methods accept a context object, and related tests and server-side logic are updated to pass and verify user-specific context during feature flag checks.

Changes

File(s) Change Summary
.../src/context/FlagProvider/FlagProvider.tsx Refactored to export UserAwareFlagProvider that injects user ID into flag context; FlagProvider now uses this component.
.../src/context/FlagProvider/FlagProvider.spec.tsx Added test verifying that UserAwareFlagProvider passes user ID to the flag provider.
.../src/pages/_app.tsx Moved FlagProvider to wrap wallet-related providers within UserProviders; adjusted provider nesting accordingly.
.../src/pages/alerts/notification-channels/[id]/index.tsx Server-side props now retrieve session and user before performing flag check with user context.
.../src/services/feature-flag/feature-flag.service.ts getFlag and isEnabledForCtx now accept a context object, enabling richer context (e.g., user ID) for flag evaluation.
.../src/services/feature-flag/feature-flag.service.spec.ts Tests updated to pass context object (with sessionId) to feature flag methods; expectations updated accordingly.
.../src/services/route-protector/route-protector.service.ts Refactored to check for user session before flag check; passes user ID in context to flag evaluation; introduced NOT_FOUND constant.
.../src/services/route-protector/route-protector.service.spec.ts Tests updated to provide user ID in context and assert correct arguments in feature flag checks.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant UserAwareFlagProvider
    participant useUser
    participant FlagProvider

    User->>App: Visit page
    App->>UserAwareFlagProvider: Render
    UserAwareFlagProvider->>useUser: Get current user
    useUser-->>UserAwareFlagProvider: Return user (with id)
    UserAwareFlagProvider->>FlagProvider: Provide config.context.userId = user.id
    FlagProvider-->>App: Render children with user-aware flags
Loading
sequenceDiagram
    participant SSR
    participant SessionService
    participant FeatureFlagService
    participant Unleash

    SSR->>SessionService: Get user session
    SessionService-->>SSR: Return session (with user.id)
    SSR->>FeatureFlagService: isEnabledForCtx(flag, ctx, { userId })
    FeatureFlagService->>Unleash: getFlag(flag, { userId })
    Unleash-->>FeatureFlagService: Return flag status
    FeatureFlagService-->>SSR: Return enabled/disabled
Loading

Suggested reviewers

  • baktun14

Poem

A hop, a skip, a context hop—
Now feature flags know when to stop!
With user IDs in every check,
Our code’s more clever, what the heck!
Providers nested, flags aware,
This bunny’s proud of all this care.
🐇✨

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.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-16T15_00_38_606Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2d8da8 and 5db84c0.

📒 Files selected for processing (8)
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.spec.tsx (1 hunks)
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx (1 hunks)
  • apps/deploy-web/src/pages/_app.tsx (2 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (1 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (2 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.ts (3 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts (3 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.spec.tsx
  • apps/deploy-web/src/pages/_app.tsx
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.ts
  • apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: validate-deploy-web
  • GitHub Check: test-deploy-web-build
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Jun 16, 2025

Codecov Report

Attention: Patch coverage is 78.94737% with 4 lines in your changes missing coverage. Please review.

Project coverage is 39.69%. Comparing base (fd37274) to head (5db84c0).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../pages/alerts/notification-channels/[id]/index.tsx 0.00% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1494      +/-   ##
==========================================
+ Coverage   39.65%   39.69%   +0.03%     
==========================================
  Files         869      869              
  Lines       20961    20968       +7     
  Branches     3838     3841       +3     
==========================================
+ Hits         8312     8323      +11     
+ Misses      12049    12045       -4     
  Partials      600      600              
Flag Coverage Δ *Carryforward flag
api 71.14% <ø> (ø) Carriedforward from fd37274
deploy-web 17.65% <78.94%> (+0.07%) ⬆️
notifications 89.18% <ø> (ø) Carriedforward from fd37274
provider-proxy 80.09% <ø> (ø) Carriedforward from fd37274

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...ploy-web/src/context/FlagProvider/FlagProvider.tsx 100.00% <100.00%> (+100.00%) ⬆️
apps/deploy-web/src/pages/_app.tsx 0.00% <ø> (ø)
.../src/services/feature-flag/feature-flag.service.ts 100.00% <100.00%> (ø)
...ervices/route-protector/route-protector.service.ts 100.00% <100.00%> (ø)
.../pages/alerts/notification-channels/[id]/index.tsx 0.00% <0.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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: 4

♻️ Duplicate comments (2)
apps/deploy-web/src/pages/alerts/notification-channels/new.tsx (1)

3-8: Same registeredOnly / user-id concerns apply here

Pass { registeredOnly: true } explicitly and ensure FeatureFlagService enforces it (see earlier comments).

apps/deploy-web/src/pages/alerts/index.tsx (1)

3-8: Replicate the fix from the [id] page

Keep the call signature consistent with:

export const getServerSideProps = featureFlagService.showIfEnabled("alerts", { registeredOnly: true });

but make sure the service actually checks the requirement.

🧹 Nitpick comments (2)
apps/deploy-web/src/pages/_app.tsx (1)

96-128: Nit: gigantic JSX chain hurts readability

Consider extracting the deep provider stack inside AppRoot to a <RootProviders> component to keep _app.tsx leaner.

apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx (1)

9-14: Potentially overriding essential Unleash config

UserAwareFlagProvider passes a new config object with only context. If the original provider relied on other keys (url, clientKey, appName, etc.) coming from environment variables via props spread in the old implementation, those values are now lost.

-return <FlagProviderOriginal config={{ context: { userId: user?.userId } }}>{children}</FlagProviderOriginal>;
+return (
+  <FlagProviderOriginal
+    config={{
+      ...browserEnvConfig.UNLEASH_PROVIDER_CONFIG, // ensure existing config stays intact
+      context: { userId: user?.userId }
+    }}
+  >
+    {children}
+  </FlagProviderOriginal>
+);

Double-check that the provider still receives the full configuration at runtime.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34ce28e and 983ec7e.

📒 Files selected for processing (12)
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx (1 hunks)
  • apps/deploy-web/src/pages/_app.tsx (2 hunks)
  • apps/deploy-web/src/pages/alerts/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/new.tsx (1 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (3 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.ts (3 hunks)
  • apps/deploy-web/src/services/feature-flag/index.ts (1 hunks)
  • apps/deploy-web/src/services/route-protector/index.ts (0 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts (0 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts (0 hunks)
💤 Files with no reviewable changes (3)
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts
  • apps/deploy-web/src/services/route-protector/index.ts
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (2)
apps/api/src/core/services/feature-flags/feature-flags.service.ts (1)
  • isEnabled (26-46)
apps/deploy-web/src/services/feature-flag/index.ts (1)
  • featureFlagService (7-7)
apps/deploy-web/src/services/feature-flag/index.ts (1)
apps/deploy-web/src/services/feature-flag/feature-flag.service.ts (1)
  • FeatureFlagService (8-56)
apps/deploy-web/src/pages/alerts/index.tsx (3)
apps/deploy-web/src/hoc/registered-users-only/registered-users-only.hoc.tsx (1)
  • RegisteredUsersOnly (6-22)
apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (1)
  • getServerSideProps (29-65)
apps/deploy-web/src/services/feature-flag/index.ts (1)
  • featureFlagService (7-7)
apps/deploy-web/src/pages/alerts/notification-channels/index.tsx (4)
apps/deploy-web/src/hoc/registered-users-only/registered-users-only.hoc.tsx (1)
  • RegisteredUsersOnly (6-22)
apps/deploy-web/src/components/alerts/NotificationChannelsPage.tsx (1)
  • NotificationChannelsPage (13-33)
apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (1)
  • getServerSideProps (29-65)
apps/deploy-web/src/services/feature-flag/index.ts (1)
  • featureFlagService (7-7)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test-deploy-web-build
🔇 Additional comments (4)
apps/deploy-web/src/services/feature-flag/index.ts (1)

1-8: Instantiation looks correct – nothing to flag.
getSession is injected in the right argument position and the import side-effect is negligible on the server bundle.

apps/deploy-web/src/pages/_app.tsx (1)

76-86: Provider order may starve upstream hooks of feature-flag context

FlagProvider now lives inside UserProviders, but providers such as ServicesProvider, SettingsProvider, etc. (declared in AppRoot) sit outside of UserProviders.
If any of those providers call useFlag they will crash (useFlag must be used within FlagProvider).

Two options:

-        <UserProviders>
-          <FlagProvider>
+        <FlagProvider>
+          <UserProviders>
               ...
-          </FlagProvider>
-        </UserProviders>
+          </UserProviders>
+        </FlagProvider>

or expose a light wrapper that forwards the userId down so FlagProvider can stay at the top of the tree.

Please audit the upstream providers before merging.

apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (1)

100-110: Tests now encode incorrect business semantics

The case “returns true when registeredOnly is true and user is NOT logged in” should in fact expect false once the server-side guard is properly enforced (see service comment). Otherwise unit tests will mask the regression instead of catching it.

apps/deploy-web/src/pages/alerts/notification-channels/index.tsx (1)

7-7: Server-side guard currently ineffective for anonymous users

Because registeredOnly is not honoured inside FeatureFlagService, the SSR entry point will happily serve the page (and potentially make downstream API calls) for visitors without a session when the “alerts” flag is enabled.
Until the service is fixed, the RegisteredUsersOnly HOC hides the page after the markup is sent, but the data layer remains exposed.
Re-evaluate once the service logic is corrected.

@ygrishajev ygrishajev force-pushed the feature/ff branch 3 times, most recently from c8ef1ce to b3a310d Compare June 16, 2025 13:15
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: 0

🧹 Nitpick comments (5)
apps/deploy-web/src/context/FlagProvider/FlagProvider.spec.tsx (2)

10-16: Prefer explicit typings over any for stronger type-safety

Using any in test helpers bypasses TypeScript’s guarantees and can silently mask refactor breakage.
A minimal explicit interface keeps the test self-documenting while remaining lightweight.

-const customFlagProvider = ({ config, children }: any) => (
+interface TestFlagProviderProps {
+  config: { context: { userId: string } };
+  children: React.ReactNode;
+}
+
+const customFlagProvider: React.FC<TestFlagProviderProps> = ({ config, children }) => (
   <div data-testid="flag-provider">
     {config.context.userId}
     {children}
   </div>
 );
-const customUseUser = () => testUser;
+const customUseUser = (): typeof testUser => testUser;

24-25: Assert exact content to avoid false positives

toContain("my-user-id") passes even if additional unexpected characters are present.
Checking for an exact match provides a tighter guarantee.

-expect(getByTestId("flag-provider").textContent).toContain("my-user-id");
+expect(getByTestId("flag-provider").textContent).toBe("my-user-id");
apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (3)

9-9: Prefer the dynamic class name over a literal string

Using the literal "FeatureFlagService.name" defeats the purpose of keeping the suite name in sync with future refactors (e.g. if the class is renamed).

-describe("FeatureFlagService.name", () => {
+describe(FeatureFlagService.name, () => {

100-110: Test description contradicts asserted behaviour

The test name claims it “returns true when registeredOnly is true and user is NOT logged in”, yet the expectation is false. Consider renaming for clarity.

-it("returns true when registeredOnly is true and user is NOT logged in", async () => {
+it("returns false when registeredOnly is true and user is NOT logged in", async () => {

147-154: isEnabled option should be typed as boolean

isEnabled is treated as a boolean but typed as jest.Mock, which is misleading.

-function setup(options?: { enableAll?: boolean; isEnabled?: jest.Mock; hasUserSession?: boolean }) {
+function setup(options?: { enableAll?: boolean; isEnabled?: boolean; hasUserSession?: boolean }) {
   ...
-  flagsClient.isEnabled.mockReturnValue(!!options?.isEnabled);
+  flagsClient.isEnabled.mockReturnValue(options?.isEnabled ?? false);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 983ec7e and b3a310d.

📒 Files selected for processing (13)
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.spec.tsx (1 hunks)
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx (1 hunks)
  • apps/deploy-web/src/pages/_app.tsx (2 hunks)
  • apps/deploy-web/src/pages/alerts/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/index.tsx (1 hunks)
  • apps/deploy-web/src/pages/alerts/notification-channels/new.tsx (1 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (3 hunks)
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.ts (3 hunks)
  • apps/deploy-web/src/services/feature-flag/index.ts (1 hunks)
  • apps/deploy-web/src/services/route-protector/index.ts (0 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts (0 hunks)
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts (0 hunks)
💤 Files with no reviewable changes (3)
  • apps/deploy-web/src/services/route-protector/index.ts
  • apps/deploy-web/src/services/route-protector/route-protector.service.spec.ts
  • apps/deploy-web/src/services/route-protector/route-protector.service.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/deploy-web/src/pages/alerts/index.tsx
  • apps/deploy-web/src/pages/_app.tsx
  • apps/deploy-web/src/pages/alerts/notification-channels/new.tsx
  • apps/deploy-web/src/services/feature-flag/index.ts
  • apps/deploy-web/src/pages/alerts/notification-channels/index.tsx
  • apps/deploy-web/src/pages/alerts/notification-channels/[id]/index.tsx
  • apps/deploy-web/src/context/FlagProvider/FlagProvider.tsx
  • apps/deploy-web/src/services/feature-flag/feature-flag.service.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: validate-deploy-web
  • GitHub Check: test-deploy-web-build
🔇 Additional comments (8)
apps/deploy-web/src/services/feature-flag/feature-flag.service.spec.ts (8)

13-15: Unit-test paths look sound

The happy-path assertions for getFlag correctly verify that the short-circuit and evaluation flows return true.

Also applies to: 21-28


60-62: Session ID propagation verified correctly

The expectation on getFlag guarantees that the session ID extracted from cookies is forwarded, covering an easy-to-miss detail.


64-98: Comprehensive coverage of registeredOnly and userId options

Nice spread of scenarios ensuring the correct precedence between explicit userId, session-derived IDs, and the early-exit when no user is present.


112-122: Edge-case (no userId) handled well

The negative path is asserted correctly and guards against accidental access for anonymous users when registeredOnly is requested.


128-133: showIfEnabled spy arguments verified

Ensuring options are forwarded (even when undefined) protects against silent API drift in the wrapper helper.


138-143: Negative branch of showIfEnabled covered

Good to see both branches exercised; keeps the helper honest.


159-161: Mocking getSession inline is concise and flexible

The helper cleanly toggles between “logged-in” and anonymous scenarios without leaking complexity into the individual tests.


164-164: Return object from setup exposes all useful test doubles

Makes downstream assertions straightforward—good practice.

stalniy
stalniy previously approved these changes Jun 16, 2025
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

Comments