Skip to content

feat: adds feature flags support to console-api's notifications proxy#1472

Merged
stalniy merged 2 commits intomainfrom
feat/feature-flags-notifications
Jun 16, 2025
Merged

feat: adds feature flags support to console-api's notifications proxy#1472
stalniy merged 2 commits intomainfrom
feat/feature-flags-notifications

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Jun 12, 2025

Why

refs: #1454

Summary by CodeRabbit

  • New Features

    • Introduced feature flag management using Unleash, including a new service for evaluating feature flags and environment configuration options for feature flagging.
    • Added feature-flag-based access control to alert mutation endpoints in the notifications API.
    • Added a mock OAuth2 server to the development Docker Compose setup for improved authentication testing.
    • Implemented graceful shutdown handling for the API server to ensure proper cleanup on termination signals.
  • Bug Fixes

    • Improved type safety and non-null assertions in authentication and controller services to prevent unexpected null values.
  • Tests

    • Added comprehensive tests for the new feature flag service, covering configuration and behavior scenarios.
    • Added tests for the server shutdown process to ensure idempotent and error-resilient behavior.
  • Chores

    • Updated and added dependencies to support feature flagging and related services.
    • Expanded environment configuration schema with new variables and validation rules for feature flagging integration.

@stalniy stalniy requested a review from a team as a code owner June 12, 2025 13:41
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Walkthrough

This update introduces feature flag support using Unleash, adds environment configuration for feature flags, and implements a new FeatureFlagsService for flag evaluation. Proxy route logic is updated to guard alert mutation endpoints with feature flags. Type safety improvements are made in execution context handling, and a mock OAuth2 server is added for development. Graceful shutdown handling is implemented for the API server.

Changes

File(s) Change Summary
apps/api/package.json Updated tsyringe version; added unleash-client dependency.
apps/api/src/app.ts Added graceful shutdown handling to close server and dispose DI container on termination signals; initialized FeatureFlagsService.
apps/api/src/auth/services/auth.service.ts Added non-null assertions to currentUser and ability getters.
apps/api/src/core/config/env.config.ts Added Unleash and feature flag environment variables; added NETWORK enum; improved schema with validation and conditional checks.
apps/api/src/core/services/core-config/core-config.service.ts Changed import paths to relative.
apps/api/src/core/services/config/config.service.ts Expanded generic type constraints to accept schemas wrapped with ZodEffects for enhanced validation support.
apps/api/src/core/services/execution-context/execution-context.service.ts Introduced strong typing for context storage and generics for set/get methods.
apps/api/src/core/services/feature-flags/feature-flags.service.ts Added new FeatureFlagsService class for Unleash integration and feature flag evaluation.
apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts Added tests for FeatureFlagsService covering Unleash integration and context handling.
apps/api/src/core/services/feature-flags/feature-flags.ts Added FeatureFlags constant and FeatureFlagValue type alias for feature flag keys.
apps/api/src/core/services/shutdown-server/shutdown-server.ts Added shutdownServer function for graceful server shutdown with error handling and idempotency.
apps/api/src/core/services/shutdown-server/shutdown-server.spec.ts Added tests for shutdownServer function covering concurrency and error scenarios.
apps/api/src/notifications/routes/proxy/proxy.route.ts Refactored proxy route: typed context, conditional header, added feature-flag-guarded route handler, updated routing for alerts.
apps/api/src/notifications/routes/proxy/proxy.route.spec.ts Extended mocked context with a get method to align with AppContext expectations.
apps/api/src/user/controllers/user/user.controller.ts Added non-null assertion to httpContext getter.
packages/docker/docker-compose.dev.yml Added mock-oauth2-server service for local development with custom token rules and login page.
apps/api/env/.env.functional.test Removed ALLOW_ANONYMOUS_USER_TRIAL; added FEATURE_FLAGS_ENABLE_ALL=true.
apps/api/env/.env.local.sample Removed ALLOW_ANONYMOUS_USER_TRIAL; added trailing newline.
apps/api/env/.env.production Removed ALLOW_ANONYMOUS_USER_TRIAL line.
apps/api/env/.env.sample Added FEATURE_FLAGS_ENABLE_ALL, UNLEASH_SERVER_API_TOKEN, and UNLEASH_SERVER_API_URL variables.
apps/api/env/.env.unit.test Added FEATURE_FLAGS_ENABLE_ALL=true.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API_Server
    participant FeatureFlagsService
    participant UnleashClient

    Client->>API_Server: Request to /v1/alerts (POST/PATCH)
    API_Server->>FeatureFlagsService: isEnabled('NOTIFICATIONS_ALERT_MUTATION')
    FeatureFlagsService->>UnleashClient: check feature flag with context
    UnleashClient-->>FeatureFlagsService: flag enabled/disabled
    FeatureFlagsService-->>API_Server: true/false
    alt Feature enabled
        API_Server->>Client: Process request (proxy)
    else Feature disabled
        API_Server->>Client: 405 Method Not Allowed
    end
Loading

Suggested reviewers

  • ygrishajev
  • baktun14

Poem

In fields of code where features bloom,
A rabbit hops with flags in tune.
Unleash now guards the proxy gate,
While context types illuminate.
With OAuth mock and tests anew,
This patch brings springtime’s clever hue!
🐇✨

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-16T02_59_23_712Z-debug-0.log

✨ 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 12, 2025

Codecov Report

Attention: Patch coverage is 88.50575% with 10 lines in your changes missing coverage. Please review.

Project coverage is 39.62%. Comparing base (edbd80f) to head (a4bc5ea).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
apps/api/src/app.ts 25.00% 6 Missing ⚠️
.../api/src/notifications/routes/proxy/proxy.route.ts 85.71% 2 Missing ⚠️
apps/api/src/core/config/env.config.ts 80.00% 1 Missing ⚠️
...re/services/feature-flags/feature-flags.service.ts 96.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1472      +/-   ##
==========================================
+ Coverage   39.44%   39.62%   +0.17%     
==========================================
  Files         866      869       +3     
  Lines       20888    20958      +70     
  Branches     3825     3839      +14     
==========================================
+ Hits         8239     8304      +65     
- Misses      11915    12470     +555     
+ Partials      734      184     -550     
Flag Coverage Δ
api 71.05% <88.50%> (+0.24%) ⬆️
deploy-web 17.58% <ø> (ø)
notifications 89.18% <ø> (ø)
provider-proxy 80.09% <ø> (ø)
Files with missing lines Coverage Δ
apps/api/src/auth/services/auth.service.ts 100.00% <100.00%> (ø)
...pps/api/src/core/services/config/config.service.ts 100.00% <100.00%> (ø)
...c/core/services/core-config/core-config.service.ts 100.00% <100.00%> (ø)
...ces/execution-context/execution-context.service.ts 94.44% <100.00%> (ø)
...i/src/core/services/feature-flags/feature-flags.ts 100.00% <100.00%> (ø)
...c/core/services/shutdown-server/shutdown-server.ts 100.00% <100.00%> (ø)
...s/api/src/user/controllers/user/user.controller.ts 100.00% <100.00%> (ø)
apps/api/src/core/config/env.config.ts 85.71% <80.00%> (-14.29%) ⬇️
...re/services/feature-flags/feature-flags.service.ts 96.87% <96.87%> (ø)
.../api/src/notifications/routes/proxy/proxy.route.ts 95.55% <85.71%> (-4.45%) ⬇️
... and 1 more

... and 182 files 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.

@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from 390683c to 2a7b09b Compare June 12, 2025 13:45
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: 10

🔭 Outside diff range comments (2)
apps/api/src/core/services/execution-context/execution-context.service.ts (1)

36-43: 🛠️ Refactor suggestion

Simplify runWithContext implementation
AsyncLocalStorage.run already propagates the promise result; the extra new Promise wrapper and redundant getStore call add overhead and hide stack traces.

-async runWithContext<R>(cb: (...args: any[]) => Promise<R>): Promise<R> {
-  return await new Promise((resolve, reject) => {
-    this.storage.run(new Map(), () => {
-      this.storage.getStore();
-      cb().then(resolve).catch(reject);
-    });
-  });
-}
+runWithContext<R>(cb: () => Promise<R>): Promise<R> {
+  return this.storage.run(new Map(), cb);
+}
apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (1)

94-105: 🛠️ Refactor suggestion

Avoid unknown as AppContext; provide a typed factory for safer mocks

Casting the entire object to unknown as AppContext defeats the stronger typings you just introduced and can let real-world regressions slip through tests.
Create a tiny helper that builds a minimal, properly-typed AppContext stub instead of the blanket cast.

-const context = {
+const context: AppContext = {
   req: { … },
   get: jest.fn().mockReturnValue(undefined)
-} as unknown as AppContext;
+} as AppContext; // ✅ keeps compile-time checks

(If other props are mandatory, stub them explicitly so the compiler tells you when the contract changes.)

🧹 Nitpick comments (4)
apps/api/package.json (1)

102-104: Pin unleash-client & audit tsyringe bump
unleash-client@^6.6.0 introduces a new major peer-dependency on Node ≥18; make sure your runtime matrix in CI/CD reflects that.
• Moving tsyringe to ^4.10.0 pulls in ESM‐ready internals that broke some transpile pipelines in <4.9 (see issues #299/#301). Verify bundle builds still tree-shake correctly.

If you want deterministic builds (recommended for infra services) pin exact versions and rely on Renovate/Dependabot for upgrades:

-    "tsyringe": "^4.10.0",
-    "unleash-client": "^6.6.0",
+    "tsyringe": "4.10.0",
+    "unleash-client": "6.6.0",
apps/api/src/core/services/feature-flags/feature-flags.ts (1)

1-5: Expose a “key” union for cleaner type-guarding
Down-stream code often needs "NOTIFICATIONS_ALERT_MUTATION" itself rather than the mapped value. Consider adding

 export const FeatureFlags = {
   NOTIFICATIONS_ALERT_MUTATION: "notifications_general_alerts_create_update"
 } as const;

+export type FeatureFlagKey = keyof typeof FeatureFlags;
 export type FeatureFlagValue = (typeof FeatureFlags)[keyof typeof FeatureFlags];

This lets consumers accept either the key or the value with proper inference and keeps enum-like ergonomics.

apps/api/src/core/services/feature-flags/feature-flags.service.ts (1)

61-64: Indefinite wait for Unleash synchronization

await new Promise(... once("synchronized") ...) has no timeout. If the Unleash server is unreachable the request thread can hang forever, leading to request pile-ups.

Add a reasonable timeout (e.g., 5-10 s) and fail gracefully/log a warning so the application keeps serving requests.

apps/api/src/notifications/routes/proxy/proxy.route.ts (1)

57-64: Resolve the service once and reuse

container.resolve(FeatureFlagsService) is invoked on every request, incurring DI overhead. Consider resolving once outside the handler (or injecting via Hono context middleware) to reuse the singleton instance.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 59d0a7c and 2a7b09b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (2 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: validate-notifications
  • GitHub Check: validate-api
  • GitHub Check: test-api-build
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-deploy-web-build
  • GitHub Check: test-stats-web-build
  • GitHub Check: Validate local packages
🔇 Additional comments (3)
apps/api/src/core/services/core-config/core-config.service.ts (1)

3-4: Confirm relative import paths compile for consumers
Switching to relative paths fixes intra-package references but can break TypeScript path-aliasing for external consumers if @src/* is relied upon elsewhere. Run tsc -p tsconfig.strict.json and ensure no duplicate modules are emitted.

packages/docker/docker-compose.dev.yml (1)

65-104:

Details

✅ Verification successful

YAML indentation & host-port clash check

  1. The mock-oauth2-server block is aligned with provider-console, not under services:. Make sure it has the same two-space indent as the other top-level services to avoid a parsing error.
  2. Port 8080 is a common choice; verify no other service in this compose file (or on your dev host) already binds it to prevent startup failures.

Example fix:

  mock-oauth2-server:
    image: ghcr.io/navikt/mock-oauth2-server:2.1.10
    ports:
      - "8080:8080"
    

🏁 Script executed:

#!/bin/bash
echo "=== Services sections ==="
rg -n "^services:" packages/docker/docker-compose.dev.yml || true

echo -e "\n=== Top of file (1-50) ==="
sed -n '1,50p' packages/docker/docker-compose.dev.yml || true

echo -e "\n=== Lines 60-110 ==="
sed -n '60,110p' packages/docker/docker-compose.dev.yml || true

echo -e "\n=== Occurrences of port 8080 ==="
rg -n "8080" packages/docker/docker-compose.dev.yml || true

Length of output: 3012


No changes required: indentation and port binding verified

Both checks pass as-is:

  • The mock-oauth2-server service is already indented two spaces under services:.
  • Port 8080 only appears once in this compose file, so there’s no intra-file conflict.
apps/api/src/notifications/routes/proxy/proxy.route.ts (1)

39-41: Nice defensive check

Conditionally setting "x-owner-address" avoids sending empty headers – good catch.

baktun14
baktun14 previously approved these changes Jun 12, 2025
@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from 2a7b09b to cd2ffd3 Compare June 13, 2025 01:53
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 (6)
apps/api/src/core/services/config/config.service.ts (2)

3-10: Avoid the banned {} type – tighten the default for C

The Biome linter warning is right: using {} as a type effectively means “any non-nullish value”, defeating type-safety. A minimal fix is to default to an explicit empty-object shape instead of {}.

-export class ConfigService<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any> = {}> {
+export class ConfigService<
+  E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>,
+  C extends Record<string, unknown> = Record<string, never>
+> {

Besides satisfying the linter, this keeps external config strongly typed and prevents accidental misuse.

🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


12-17: options.config can be undefined – defensive spread would be safer

If a caller omits config the spread ...options.config is fine at runtime (it becomes undefined → ignored) but TypeScript still treats options.config as possibly undefined.
A tiny improvement:

-  ...options.config,
+  ...(options.config ?? {}),

This removes the implicit any on the spread operand and clarifies intent.

apps/api/src/app.ts (1)

178-187: Graceful shutdown handler looks good – one micro-nit

Nice job wiring SIGINT/SIGTERM into server.close(); this resolves the earlier feedback 👏.
Consider removing the isShuttingDown = false reset inside the callback. Once the server is closed successfully we should stay in the “shutdown” state; flipping it back to false re-opens the door for a second shutdown attempt (harmless but confusing).

-    } finally {
-      isShuttingDown = false;
-    }
+    }

Purely cosmetic – feel free to ignore if you prefer the current semantics.

apps/api/src/core/config/env.config.ts (3)

19-21: Validate UNLEASH_SERVER_API_URL as a URL

Right now the variable is only z.string(). A malformed value will surface later when the Unleash client attempts to connect. Adding .url() gives immediate feedback at boot-time:

-    UNLEASH_SERVER_API_URL: z.string().optional(),
+    UNLEASH_SERVER_API_URL: z.string().url().optional(),

Same applies to any other env vars that are expected to be URLs.


22-26: Boolean env var conversion – small readability tweak

Using .transform(value => value === "true") works, but z.coerce.boolean() makes the intent clearer and accepts "1"/"0"/"true"/"false" out of the box:

-    FEATURE_FLAGS_ENABLE_ALL: z
-      .string()
-      .default("false")
-      .transform(value => value === "true")
+    FEATURE_FLAGS_ENABLE_ALL: z
+      .coerce.boolean()
+      .default(false)

Not critical, just a readability win.


27-34: Super-refine guard is solid – consider including the var names in the error path

Currently the error message is attached to the root; attaching it to the offending fields improves UX in config dashboards:

-      ctx.addIssue({
+      ctx.addIssue({
         code: z.ZodIssueCode.custom,
         message: "UNLEASH_SERVER_API_URL and UNLEASH_SERVER_API_TOKEN are required when FEATURE_FLAGS_ENABLE_ALL is false",
+        path: ["UNLEASH_SERVER_API_URL"]
       });

(duplicate for the token).
Optional, but surfaces nicely in many config UIs.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7b09b and cd2ffd3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (3 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/config/config.service.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/api/package.json
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts
  • apps/api/src/core/services/core-config/core-config.service.ts
  • apps/api/src/auth/services/auth.service.ts
  • apps/api/src/core/services/feature-flags/feature-flags.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • apps/api/src/user/controllers/user/user.controller.ts
  • packages/docker/docker-compose.dev.yml
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
  • apps/api/src/core/services/execution-context/execution-context.service.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/api/src/core/config/env.config.ts (1)
apps/api/src/chain/config/env.config.ts (1)
  • envSchema (3-5)
🪛 Biome (1.9.4)
apps/api/src/core/services/config/config.service.ts

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: validate-api
  • GitHub Check: test-api-build
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-deploy-web-build
  • GitHub Check: validate-deploy-web
  • GitHub Check: test-provider-proxy-build
  • GitHub Check: validate-provider-proxy
  • GitHub Check: test-stats-web-build
  • GitHub Check: validate-stats-web
  • GitHub Check: validate-notifications
  • GitHub Check: test-indexer-build
  • GitHub Check: Validate local packages

@stalniy stalniy force-pushed the feat/feature-flags-notifications branch 2 times, most recently from 4b6f41b to 8cc2687 Compare June 13, 2025 02:10
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

♻️ Duplicate comments (1)
apps/api/src/core/services/feature-flags/feature-flags.service.ts (1)

47-53: Hard assertion will crash the app on mis-configuration

This issue was already identified in a previous review. The assert statement will crash the entire application if configuration is missing.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cd2ffd3 and 8cc2687.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (2 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/config/config.service.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/api/package.json
  • apps/api/src/core/services/core-config/core-config.service.ts
  • apps/api/src/app.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts
  • apps/api/src/auth/services/auth.service.ts
  • apps/api/src/user/controllers/user/user.controller.ts
  • apps/api/src/core/services/feature-flags/feature-flags.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
  • packages/docker/docker-compose.dev.yml
  • apps/api/src/core/config/env.config.ts
  • apps/api/src/core/services/execution-context/execution-context.service.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/api/src/core/services/feature-flags/feature-flags.service.ts (4)
apps/api/src/core/services/core-config/core-config.service.ts (1)
  • singleton (6-11)
apps/api/src/core/services/execution-context/execution-context.service.ts (1)
  • singleton (14-44)
apps/api/src/core/services/feature-flags/feature-flags.ts (1)
  • FeatureFlagValue (5-5)
apps/api/src/auth/services/auth.service.ts (2)
  • currentUser (12-14)
  • currentUser (16-19)
🪛 Biome (1.9.4)
apps/api/src/core/services/config/config.service.ts

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: validate-api
  • GitHub Check: validate-notifications
  • GitHub Check: test-deploy-web-build
🔇 Additional comments (6)
apps/api/src/core/services/feature-flags/feature-flags.service.ts (4)

9-24: Well-structured service with good testability!

The class is properly designed as a singleton with dependency injection and implements the Disposable interface for proper cleanup. The optional createClient parameter injection is a nice touch for testability.


26-45: Comprehensive feature flag evaluation with proper context!

The method correctly handles the global override and builds a rich context for feature flag evaluation, including user information, client details, and environment properties.


69-71: Proper cleanup implementation!

The dispose method correctly implements the Disposable interface and safely cleans up the Unleash client with destroyWithFlush.


74-76: Clean factory function for testability!

The standalone factory function provides good separation of concerns and makes the service easily testable.

apps/api/src/core/services/config/config.service.ts (2)

1-1: Correctly imported ZodEffects
Including ZodEffects in the import is required to support effect-wrapped schemas. This aligns with the new generic constraints.


3-6: Expanded generic constraint is valid
Allowing E to be either a plain ZodObject or a ZodEffects<ZodObject> correctly accommodates both simple and effect-wrapped schemas. The interface change looks good.

@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from 8cc2687 to 11eedcf Compare June 13, 2025 03: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

♻️ Duplicate comments (3)
apps/api/src/core/services/config/config.service.ts (1)

8-10: {} default type is still banned

Lint keeps flagging this. Replace the default {} with Record<string, unknown> (or remove the default entirely) to satisfy the rule and improve clarity.

-export class ConfigService<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any> = {}> {
+export class ConfigService<
+  E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>,
+  C extends Record<string, unknown> = Record<string, unknown>
+> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

apps/api/src/core/services/feature-flags/feature-flags.service.ts (2)

26-30: Throwing on un-initialized service replicates earlier concern

assert(this.client, ...) will terminate the request path with a 500 if initialize() is forgotten. Past feedback proposed a safer fallback (e.g., return false and log). Consider adopting it.


52-63: Hard assertion & connection failure handling unchanged

The strict assert(url && token, …) and the un-guarded synchronized/error promise still risk crashing the whole process on mis-configuration or network glitches. Previous review supplied a controlled-startup and try/catch pattern—please revisit.

🧹 Nitpick comments (1)
apps/api/src/core/services/config/config.service.ts (1)

3-6: Reduce any usage in generics

C extends Record<string, any> silently loses type-safety because every property becomes any.
Prefer unknown to force callers to narrow or define a proper interface.

-interface ConfigServiceOptions<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any>> {
+interface ConfigServiceOptions<
+  E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>,
+  C extends Record<string, unknown>
+> {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8cc2687 and 11eedcf.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • apps/api/env/.env.functional.test (1 hunks)
  • apps/api/env/.env.local.sample (1 hunks)
  • apps/api/env/.env.production (1 hunks)
  • apps/api/env/.env.sample (1 hunks)
  • apps/api/env/.env.unit.test (1 hunks)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (3 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/config/config.service.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
✅ Files skipped from review due to trivial changes (5)
  • apps/api/env/.env.unit.test
  • apps/api/env/.env.functional.test
  • apps/api/env/.env.production
  • apps/api/env/.env.local.sample
  • apps/api/env/.env.sample
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/api/src/core/services/core-config/core-config.service.ts
  • apps/api/src/user/controllers/user/user.controller.ts
  • apps/api/package.json
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts
  • apps/api/src/auth/services/auth.service.ts
  • apps/api/src/app.ts
  • packages/docker/docker-compose.dev.yml
  • apps/api/src/core/services/feature-flags/feature-flags.ts
  • apps/api/src/core/services/execution-context/execution-context.service.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
  • apps/api/src/core/config/env.config.ts
🧰 Additional context used
🧠 Learnings (1)
apps/api/src/core/services/config/config.service.ts (1)
Learnt from: stalniy
PR: akash-network/console#1362
File: apps/api/src/core/services/config/config.service.ts:8-9
Timestamp: 2025-05-24T04:25:48.222Z
Learning: In the ConfigService class in apps/api/src/core/services/config/config.service.ts, the `{}` type is used as the default for generic parameter `C` because it's intersected with `z.infer<E>` to create `C & z.infer<E>`. Using `Record<string, never>` instead would break type checking for the merged types since it represents an object that cannot have any properties.
🪛 Biome (1.9.4)
apps/api/src/core/services/config/config.service.ts

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test-stats-web-build
  • GitHub Check: validate-provider-proxy
  • GitHub Check: validate-notifications
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-api-build
  • GitHub Check: validate-api
  • GitHub Check: test-indexer-build
  • GitHub Check: validate-deploy-web
  • GitHub Check: test-deploy-web-build
  • GitHub Check: Validate local packages
🔇 Additional comments (1)
apps/api/src/core/services/feature-flags/feature-flags.service.ts (1)

77-79:

Details

❌ Incorrect review comment

Potential misuse of Unleash client constructor

unleash-client’s public API is initialize(config), not new Unleash(config).
Using the non-documented class may break on library updates.

-import { Unleash, UnleashConfig } from "unleash-client";
+import { initialize as unleashInitialize, UnleashConfig, Unleash } from "unleash-client";-export function createUnleashClient(config: UnleashConfig): Unleash {
-  return new Unleash(config);
+export function createUnleashClient(config: UnleashConfig): Unleash {
+  return unleashInitialize(config);
 }

🌐 Web query:

What is the recommended way to create a client instance with the npm package "unleash-client"?

💡 Result:

The recommended way to create a client instance with the npm package "unleash-client" involves requiring or importing the package and then instantiating a new client with the desired configuration. Typically, you configure the client with the URL of your Unleash server and an application name.

Here is an example in Node.js:

const { UnleashClient } = require('unleash-client');

const unleash = new UnleashClient({
  url: 'https://unleash-server-url/api/',
  appName: 'my-nodejs-app',
  instanceId: 'my-unique-instance-id', 
});

unleash.start();

In this example:

  • url is the URL to your Unleash server API.
  • appName is a string used to identify your application.
  • instanceId is a unique identifier for the client instance (optional but recommended for identifying the client).

After creating the client, you start it with unleash.start().

You can then use the client instance methods to check feature toggles, such as:

const isEnabled = unleash.isEnabled('someFeatureToggle');

If you need a more specific or updated example, I can retrieve the latest usage details from the official documentation or npm page. Would you like me to do that?


Incorrect recommendation: use new Unleash(config) instead of a non-existent initialize()
The unleash-client package exports a client class (Unleash or UnleashClient) that you instantiate with new Unleash(config) and then call .start(). It does not export an initialize() helper. The existing code is already using the correct public API.

• File: apps/api/src/core/services/feature-flags/feature-flags.service.ts (lines 77–79):

export function createUnleashClient(config: UnleashConfig): Unleash {
  return new Unleash(config);
}

Likely an incorrect or invalid review comment.

@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from c7d3393 to 1c27ae9 Compare June 13, 2025 07:56
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 (2)
apps/api/src/core/services/config/config.service.ts (2)

3-6: Avoid repeating the verbose union – extract a helper alias

ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>> now appears twice (here and on the class below). Creating a single alias clarifies intent and prevents the two occurrences from silently diverging in the future.

-import type { z, ZodEffects, ZodObject, ZodRawShape } from "zod";
+import type { z, ZodEffects, ZodObject, ZodRawShape } from "zod";

+type EnvSchema =
+  | ZodObject<ZodRawShape>
+  | ZodEffects<ZodObject<ZodRawShape>>;
 
-interface ConfigServiceOptions<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any>> {
+interface ConfigServiceOptions<
+  E extends EnvSchema,
+  C extends Record<string, any>
+> {

9-10: Tighten the generic & satisfy the “no banned types” rule

Record<string, any> widens the config shape and defeats type-safety, while the default {} triggers the linter.
Switching to Record<string, unknown> keeps the flexibility without opting out of type checking, and we can still default to an empty object via the type parameter itself.

-export class ConfigService<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any> = {}> {
+export class ConfigService<
+  E extends EnvSchema,
+  C extends Record<string, unknown> = Record<string, unknown>
+> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 11eedcf and 1c27ae9.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • apps/api/env/.env.functional.test (1 hunks)
  • apps/api/env/.env.local.sample (1 hunks)
  • apps/api/env/.env.production (1 hunks)
  • apps/api/env/.env.sample (1 hunks)
  • apps/api/env/.env.unit.test (1 hunks)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (3 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/config/config.service.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/api/env/.env.production
🚧 Files skipped from review as they are similar to previous changes (17)
  • apps/api/env/.env.functional.test
  • apps/api/src/core/services/core-config/core-config.service.ts
  • apps/api/env/.env.local.sample
  • apps/api/package.json
  • apps/api/env/.env.unit.test
  • apps/api/src/auth/services/auth.service.ts
  • apps/api/src/user/controllers/user/user.controller.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts
  • apps/api/env/.env.sample
  • apps/api/src/app.ts
  • apps/api/src/core/services/feature-flags/feature-flags.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • packages/docker/docker-compose.dev.yml
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
  • apps/api/src/core/services/execution-context/execution-context.service.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts
  • apps/api/src/core/config/env.config.ts
🧰 Additional context used
🧠 Learnings (1)
apps/api/src/core/services/config/config.service.ts (1)
Learnt from: stalniy
PR: akash-network/console#1362
File: apps/api/src/core/services/config/config.service.ts:8-9
Timestamp: 2025-05-24T04:25:48.222Z
Learning: In the ConfigService class in apps/api/src/core/services/config/config.service.ts, the `{}` type is used as the default for generic parameter `C` because it's intersected with `z.infer<E>` to create `C & z.infer<E>`. Using `Record<string, never>` instead would break type checking for the merged types since it represents an object that cannot have any properties.
🪛 Biome (1.9.4)
apps/api/src/core/services/config/config.service.ts

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: validate-api
  • GitHub Check: test-api-build
  • GitHub Check: validate-notifications
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-deploy-web-build
🔇 Additional comments (1)
apps/api/src/core/services/config/config.service.ts (1)

1-1: Import extension looks good

Adding ZodEffects is the right move to support effect-wrapped schemas.

ygrishajev
ygrishajev previously approved these changes Jun 13, 2025
@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from 1c27ae9 to ef21de4 Compare June 16, 2025 02:23
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

🔭 Outside diff range comments (1)
apps/api/src/core/services/config/config.service.ts (1)

13-16: ⚠️ Potential issue

Spreading undefined at runtime causes a crash

When envSchema is absent, options.envSchema?.parse(process.env) returns undefined, and
{ ...undefined } throws TypeError: Cannot convert undefined or null to object.

-      ...options.config,
-      ...options.envSchema?.parse(process.env)
+      ...options.config,
+      ...(options.envSchema ? options.envSchema.parse(process.env) : {})
♻️ Duplicate comments (1)
apps/api/src/core/services/config/config.service.ts (1)

8-10: 🛠️ Refactor suggestion

Replace banned type {} with Record<string, unknown>

Biome still flags {} even though ESLint is suppressed.
Switching to Record<string, unknown> keeps full property flexibility while satisfying lint rules and avoids the never-property pitfall of Record<string, never> mentioned in previous discussions.

-export class ConfigService<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any> = {}> {
+export class ConfigService<
+  E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>,
+  C extends Record<string, unknown> = Record<string, unknown>
+> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🧹 Nitpick comments (6)
apps/api/env/.env.sample (1)

39-41: Add a dedicated Feature Flags section with inline docs

Group these new vars under a # Feature Flags header and include brief descriptions to improve clarity:

@@
 NOTIFICATIONS_API_BASE_URL=
+# Feature Flags
+# Globally enable all flags when `true`; set to `false` to use Unleash evaluations.
 FEATURE_FLAGS_ENABLE_ALL=true
+# Unleash API token (required when FEATURE_FLAGS_ENABLE_ALL=false).
 UNLEASH_SERVER_API_TOKEN=
+# Base URL of the Unleash server API (e.g., https://unleash.example.com/api).
 UNLEASH_SERVER_API_URL=
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 39-39: [UnorderedKey] The FEATURE_FLAGS_ENABLE_ALL key should go before the FEE_ALLOWANCE_REFILL_AMOUNT key


[warning] 40-40: [UnorderedKey] The UNLEASH_SERVER_API_TOKEN key should go before the WEBSITE_URL key


[warning] 41-41: [UnorderedKey] The UNLEASH_SERVER_API_URL key should go before the WEBSITE_URL key

apps/api/env/.env.unit.test (1)

28-28: Validate new NOTIFICATIONS_API_BASE_URL integration and address key ordering warning.
Ensure NOTIFICATIONS_API_BASE_URL is registered in env.config.ts and consumed by the notifications proxy logic. Also reorder this key to satisfy the dotenv-linter UnorderedKey warning for consistency.

🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 28-28: [UnorderedKey] The NOTIFICATIONS_API_BASE_URL key should go before the POSTGRES_DB_URI key

apps/api/src/core/services/shutdown-server/shutdown-server.ts (1)

24-26: Keeping isShuttingDown false after completion re-opens the gate

Once the shutdown sequence has completed, further invocations of shutdownServer() will run again because the flag is reset.
When the process is already on its way out this is harmless, but during tests or when multiple subsystems reuse the helper it can lead to duplicate disposal attempts.

Consider leaving the flag true (or introducing a separate hasShutdown flag) so subsequent calls are always short-circuited:

-          isShuttingDown = false;
+          // keep the flag set to prevent repeat shutdown attempts
+          // isShuttingDown = false;
apps/api/src/core/services/shutdown-server/shutdown-server.spec.ts (2)

15-20: Test still passes even if the early-return bug sneaks in

Because return; yields an implicit resolved promise, the concurrency test would keep passing even if shutdownServer() stopped returning a promise altogether for the second and subsequent invocations.

Add an explicit assertion that every shutdownServer() call yields a Promise to strengthen the contract:

const results = Array.from({ length: 5 }, () => shutdownServer(server, appLogger, onShutdown));
results.forEach(r => expect(r).toBeInstanceOf(Promise));
await Promise.all(results);

40-47: Missing verification that the outer promise fulfills

The third test checks that the error is logged, but it doesn’t assert that shutdownServer() eventually resolves.
Add an assertion such as:

await expect(shutdownServer(server, appLogger, onShutdown)).resolves.toBeUndefined();

to catch potential hangs introduced by future refactors.

apps/api/src/core/services/config/config.service.ts (1)

3-6: Add a default for C in ConfigServiceOptions to match the class generic

ConfigService gives C a default, but ConfigServiceOptions does not.
Without it, callers have to specify both generics when they only care about E, which is inconsistent and noisy.

-interface ConfigServiceOptions<E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>, C extends Record<string, any>> {
+interface ConfigServiceOptions<
+  E extends ZodObject<ZodRawShape> | ZodEffects<ZodObject<ZodRawShape>>,
+  C extends Record<string, unknown> = Record<string, unknown>
+> {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c27ae9 and ef21de4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (21)
  • apps/api/env/.env.functional.test (1 hunks)
  • apps/api/env/.env.local.sample (1 hunks)
  • apps/api/env/.env.production (1 hunks)
  • apps/api/env/.env.sample (1 hunks)
  • apps/api/env/.env.unit.test (1 hunks)
  • apps/api/package.json (1 hunks)
  • apps/api/src/app.ts (2 hunks)
  • apps/api/src/auth/services/auth.service.ts (1 hunks)
  • apps/api/src/core/config/env.config.ts (1 hunks)
  • apps/api/src/core/services/config/config.service.ts (1 hunks)
  • apps/api/src/core/services/core-config/core-config.service.ts (1 hunks)
  • apps/api/src/core/services/execution-context/execution-context.service.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts (1 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.ts (1 hunks)
  • apps/api/src/core/services/shutdown-server/shutdown-server.spec.ts (1 hunks)
  • apps/api/src/core/services/shutdown-server/shutdown-server.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts (2 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
  • apps/api/src/user/controllers/user/user.controller.ts (1 hunks)
  • packages/docker/docker-compose.dev.yml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/api/env/.env.functional.test
🚧 Files skipped from review as they are similar to previous changes (15)
  • apps/api/env/.env.local.sample
  • apps/api/src/core/services/core-config/core-config.service.ts
  • apps/api/package.json
  • apps/api/env/.env.production
  • apps/api/src/user/controllers/user/user.controller.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.spec.ts
  • apps/api/src/app.ts
  • apps/api/src/auth/services/auth.service.ts
  • apps/api/src/core/services/feature-flags/feature-flags.ts
  • apps/api/src/core/services/execution-context/execution-context.service.ts
  • packages/docker/docker-compose.dev.yml
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.ts
  • apps/api/src/core/config/env.config.ts
🧰 Additional context used
🧠 Learnings (1)
apps/api/src/core/services/config/config.service.ts (1)
Learnt from: stalniy
PR: akash-network/console#1362
File: apps/api/src/core/services/config/config.service.ts:8-9
Timestamp: 2025-05-24T04:25:48.222Z
Learning: In the ConfigService class in apps/api/src/core/services/config/config.service.ts, the `{}` type is used as the default for generic parameter `C` because it's intersected with `z.infer<E>` to create `C & z.infer<E>`. Using `Record<string, never>` instead would break type checking for the merged types since it represents an object that cannot have any properties.
🧬 Code Graph Analysis (1)
apps/api/src/core/services/shutdown-server/shutdown-server.ts (2)
apps/notifications/src/common/services/shutdown/shutdown.service.ts (1)
  • onShutdown (8-10)
packages/logging/src/servicies/logger/logger.service.ts (1)
  • error (107-109)
🪛 dotenv-linter (3.3.0)
apps/api/env/.env.sample

[warning] 39-39: [UnorderedKey] The FEATURE_FLAGS_ENABLE_ALL key should go before the FEE_ALLOWANCE_REFILL_AMOUNT key


[warning] 40-40: [UnorderedKey] The UNLEASH_SERVER_API_TOKEN key should go before the WEBSITE_URL key


[warning] 41-41: [UnorderedKey] The UNLEASH_SERVER_API_URL key should go before the WEBSITE_URL key

apps/api/env/.env.unit.test

[warning] 28-28: [UnorderedKey] The NOTIFICATIONS_API_BASE_URL key should go before the POSTGRES_DB_URI key

🪛 Biome (1.9.4)
apps/api/src/core/services/config/config.service.ts

[error] 9-9: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-provider-proxy-build
  • GitHub Check: validate-provider-proxy
  • GitHub Check: test-indexer-build
  • GitHub Check: validate-deploy-web
  • GitHub Check: test-deploy-web-build
  • GitHub Check: validate-stats-web
  • GitHub Check: test-stats-web-build
  • GitHub Check: validate-notifications
  • GitHub Check: test-api-build
  • GitHub Check: validate-api
  • GitHub Check: Validate local packages
🔇 Additional comments (2)
apps/api/env/.env.unit.test (1)

30-30: Enable all feature flags for unit tests
Setting FEATURE_FLAGS_ENABLE_ALL=true ensures feature flags are globally enabled during unit tests, aligning with the introduced FeatureFlagsService override.

apps/api/src/core/services/shutdown-server/shutdown-server.ts (1)

8-11: 🛠️ Refactor suggestion

Return a resolved promise when shutdown is already in-progress

return; inside an async function produces an implicit Promise<undefined>, but the consumers of shutdownServer() expect a Promise<void> that is always present.
Returning undefined can break await / Promise.all call-sites that rely on a guaranteed promise object (your tests work only because Jest tolerates it).

-  if (isShuttingDown) return;
+  if (isShuttingDown) {
+    // ensure the caller still receives a settled promise
+    return Promise.resolve();
+  }

Likely an incorrect or invalid review comment.

@stalniy stalniy force-pushed the feat/feature-flags-notifications branch from ef21de4 to a4bc5ea Compare June 16, 2025 02:57
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 (3)
apps/api/src/core/services/shutdown-server/shutdown-server.spec.ts (3)

8-20: Mock server.close asynchronously to better emulate real-world timing

jest.fn().mockImplementation(cb => cb()) executes the callback synchronously.
http.Server.close() in Node calls its callback on the next tick, so the unit test is not exercising any race conditions that might exist around asynchronous completion. Switching to setImmediate (or process.nextTick) will make the “many concurrent calls” scenario closer to production reality.

-const server = mock<ServerType>({
-  close: jest.fn().mockImplementation(cb => cb())
-});
+const server = mock<ServerType>({
+  close: jest.fn().mockImplementation(cb => {
+    // defer to next tick to mimic http.Server behaviour
+    setImmediate(cb);
+  }),
+});

This change keeps the test fast while increasing its fidelity.


24-27: Add an assertion that server.close was invoked

The error-path test checks that the error is logged, but it doesn’t verify that shutdownServer actually attempted to close the server. A quick extra expectation will guard against regressions where the close call is skipped entirely.

 await shutdownServer(server, appLogger, onShutdown);

+expect(server.close).toHaveBeenCalledTimes(1);
 expect(appLogger.error).toHaveBeenCalledWith({
   event: "SERVER_CLOSE_ERROR",
   error
 });

42-45: Avoid as jest.Mock by typing the mock function directly

The cast hides type-safety issues and can mask accidental signature changes. Provide the generic signature to jest.fn() instead:

-close: jest.fn().mockImplementation(() => {
-  throw error;
-}) as jest.Mock
+close: jest.fn<Parameters<ServerType["close"]>[0], void>(() => {
+  throw error;
+}),

This keeps strict typing intact without relying on a broad cast.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ef21de4 and a4bc5ea.

📒 Files selected for processing (5)
  • apps/api/src/app.ts (2 hunks)
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts (1 hunks)
  • apps/api/src/core/services/shutdown-server/shutdown-server.spec.ts (1 hunks)
  • apps/api/src/core/services/shutdown-server/shutdown-server.ts (1 hunks)
  • apps/api/src/notifications/routes/proxy/proxy.route.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/api/src/app.ts
  • apps/api/src/core/services/shutdown-server/shutdown-server.ts
  • apps/api/src/core/services/feature-flags/feature-flags.service.spec.ts
  • apps/api/src/notifications/routes/proxy/proxy.route.ts
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: test-stats-web-build
  • GitHub Check: validate-notifications
  • GitHub Check: test-provider-console-build
  • GitHub Check: test-deploy-web-build
  • GitHub Check: validate-deploy-web
  • GitHub Check: validate-api
  • GitHub Check: test-api-build
  • GitHub Check: test-indexer-build
  • GitHub Check: Validate local packages

@stalniy stalniy merged commit c663c55 into main Jun 16, 2025
29 checks passed
@stalniy stalniy deleted the feat/feature-flags-notifications branch June 16, 2025 03:09
stalniy added a commit that referenced this pull request Nov 20, 2025
…#1472)

* feat: adds feature flags support to console-api's notifications proxy

* chore: improve coverage
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.

3 participants

Comments