Skip to content

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Dec 25, 2025

Why

to reduce amount of uncontrolled side effects and improve testability of the app

Summary by CodeRabbit

  • Refactor

    • Switched analytics, URL and config access to a centralized dependency-injected provider (publicConfig) across the app; no UI or behavioral changes.
    • Reworked analytics and payment wiring: removed legacy singleton, analytics now provided from DI container; Stripe client now gets publishable key via unified config shape.
  • Tests

    • Updated tests to inject and mock analytics via the DI/test container for reliable analytics-related coverage.

✏️ Tip: You can customize this high-level summary in your review settings.

@stalniy stalniy requested a review from a team as a code owner December 25, 2025 11:16
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

Rewires components to obtain analytics/url/stripe services from the DI container via useServices(), introduces publicConfig in the DI container, updates AnalyticsService and StripeService constructor shapes, removes the deprecated analytics singleton, and adapts tests and DI wiring accordingly. No UI/control-flow behavior changes.

Changes

Cohort / File(s) Summary
Analytics wiring (components)
apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx, apps/deploy-web/src/components/authorizations/AllowanceModal.tsx, apps/deploy-web/src/components/authorizations/GrantModal.tsx, apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx, apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx, apps/deploy-web/src/components/deployments/DeploymentListRow.tsx, apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx, apps/deploy-web/src/components/liquidity-modal/index.tsx, apps/deploy-web/src/components/new-deployment/TemplateList.tsx, apps/deploy-web/src/components/sdl/ImportSdlModal.tsx, apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx, apps/deploy-web/src/components/settings/ExportCertificate.tsx, apps/deploy-web/src/components/templates/UserTemplate.tsx, apps/deploy-web/src/components/user/AddFundsLink.tsx, apps/deploy-web/src/components/user/UserFavorites.tsx, apps/deploy-web/src/pages/user/api-keys/index.tsx
Replaced module-level analyticsService imports with const { analyticsService } = useServices(); tracking calls unchanged.
Config destructuring / publicConfig aliasing
apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx, apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx, apps/deploy-web/src/components/sdl/RentGpusForm.tsx, apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx, apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx, apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx, apps/deploy-web/src/hooks/useStoredAnonymousUser.ts, apps/deploy-web/src/hooks/useTrialBalance.ts
Switched destructuring to { publicConfig: appConfig } = useServices() (or equivalent aliasing) so components source config from publicConfig.
UrlService → urlService instance
apps/deploy-web/src/components/user/UserProfile.tsx, apps/deploy-web/src/components/user/UserProfileLayout.tsx, apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
Removed static UrlService imports; obtain urlService from useServices() and replace UrlService.* calls with urlService.*.
Tests — analytics & useServices mocks
apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx, apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
Injected mocked AnalyticsService via test container; updated useServices test mocks to expose publicConfig instead of appConfig and removed UrlService where applicable.
Analytics core changes
apps/deploy-web/src/services/analytics/analytics.service.ts
Reworked AnalyticsService API: renamed amplitude → amplitudeClient, changed default murmurhash import and HashFn type, adjusted constructor defaults (ga/getGtag/storage), routed amplitude calls through amplitudeClient, and removed the deprecated module-level analyticsService singleton.
DI container and browser container
apps/deploy-web/src/services/app-di-container/app-di-container.ts, apps/deploy-web/src/services/app-di-container/browser-di-container.ts
Exposed publicConfig on the container, wired AnalyticsService and StripeService with environment values from publicConfig, and updated baseURL and exported surface to use publicConfig (removed old appConfig export).
Stripe service refactor
apps/deploy-web/src/services/stripe/stripe.service.ts, apps/deploy-web/src/services/stripe/stripe.service.spec.ts
Replaced browserEnvConfig usage with new config: { publishableKey } shape, made loadStripe optional in dependencies, updated constructor and dependency typing, and adjusted tests to the new constructor/config pattern.

Sequence Diagram(s)

(omitted — refactor/DI wiring without new multi-component control-flow features)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ygrishajev

Poem

🐰 I hopped through files with nimble feet,

Swapped singletons for hooks so neat,
Configs now public, services passed in one,
DI threads woven, refactor done—
A tiny carrot of code complete! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: refactoring analyticsService creation to be managed by the DI container, which is the central theme across all modified files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/analytics-service

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

@stalniy stalniy force-pushed the refactor/analytics-service branch from 6cecef4 to feac91b Compare December 25, 2025 11:17
@codecov
Copy link

codecov bot commented Dec 25, 2025

Codecov Report

❌ Patch coverage is 35.00000% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.94%. Comparing base (dccb96d) to head (b3f66bc).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
.../src/services/app-di-container/app-di-container.ts 62.50% 6 Missing ⚠️
...ploy-web/src/components/user/UserProfileLayout.tsx 0.00% 5 Missing ⚠️
...eploy-web/src/components/liquidity-modal/index.tsx 0.00% 3 Missing ⚠️
...-web/src/components/api-keys/CreateApiKeyModal.tsx 0.00% 2 Missing ⚠️
...b/src/components/authorizations/AllowanceModal.tsx 0.00% 2 Missing ⚠️
...y-web/src/components/authorizations/GrantModal.tsx 0.00% 2 Missing ⚠️
.../components/deployments/DeploymentDepositModal.tsx 0.00% 2 Missing ⚠️
.../components/deployments/DeploymentDetailTopBar.tsx 0.00% 2 Missing ⚠️
...b/src/components/deployments/DeploymentListRow.tsx 0.00% 2 Missing ⚠️
.../src/components/deployments/ShellDownloadModal.tsx 0.00% 2 Missing ⚠️
... and 15 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2418      +/-   ##
==========================================
- Coverage   51.29%   50.94%   -0.36%     
==========================================
  Files        1071     1061      -10     
  Lines       29328    29005     -323     
  Branches     6474     6436      -38     
==========================================
- Hits        15044    14776     -268     
+ Misses      13873    13818      -55     
  Partials      411      411              
Flag Coverage Δ *Carryforward flag
api 80.05% <ø> (ø) Carriedforward from dccb96d
deploy-web 31.31% <35.00%> (-0.08%) ⬇️
log-collector ?
notifications 87.94% <ø> (ø) Carriedforward from dccb96d
provider-console 81.48% <ø> (ø) Carriedforward from dccb96d
provider-proxy 84.35% <ø> (ø) Carriedforward from dccb96d

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

Files with missing lines Coverage Δ
...arding/OnboardingContainer/OnboardingContainer.tsx 79.41% <100.00%> (-0.16%) ⬇️
...rc/components/user/UserProviders/UserProviders.tsx 100.00% <100.00%> (ø)
...pps/deploy-web/src/hooks/useStoredAnonymousUser.ts 81.48% <100.00%> (ø)
...oy-web/src/services/analytics/analytics.service.ts 81.63% <100.00%> (-2.21%) ⬇️
...s/deploy-web/src/services/stripe/stripe.service.ts 80.76% <100.00%> (-1.38%) ⬇️
...src/components/deployments/DeploymentSubHeader.tsx 0.00% <0.00%> (ø)
...web/src/components/new-deployment/ManifestEdit.tsx 0.00% <0.00%> (ø)
...pps/deploy-web/src/components/sdl/RentGpusForm.tsx 0.00% <0.00%> (ø)
...ps/deploy-web/src/components/user/AddFundsLink.tsx 53.84% <50.00%> (-4.49%) ⬇️
apps/deploy-web/src/hooks/useTrialBalance.ts 0.00% <0.00%> (ø)
... and 20 more

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 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: 1

Caution

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

⚠️ Outside diff range comments (1)
apps/deploy-web/src/components/liquidity-modal/index.tsx (1)

133-146: Missing analyticsService in useMemo dependency array.

The tabsConfig memoization uses analyticsService inside onTxnComplete, but it's not listed in the dependency array. This could lead to stale closures if the service reference ever changes.

🔎 Proposed fix
-  }, [refreshBalances]);
+  }, [refreshBalances, analyticsService]);
🧹 Nitpick comments (5)
apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx (1)

58-58: Use queryByText instead of getByText in test expectations.

The test uses getByText in an expectation, but the coding guidelines specify that queryBy methods should be used instead of getBy methods in test expectations for this path pattern.

🔎 Proposed fix
-    expect(getByText(`${provider.ipRegionCode}, ${provider.ipCountryCode}`)).toBeInTheDocument();
+    const { queryByText } = setup({
+      bid,
+      provider,
+      components
+    });
+    expect(queryByText(`${provider.ipRegionCode}, ${provider.ipCountryCode}`)).toBeInTheDocument();

Note: You'll also need to update the destructuring at line 38 to use queryByText instead of getByText.

As per coding guidelines, use queryBy methods in test expectations.

apps/deploy-web/src/components/settings/ExportCertificate.tsx (1)

13-23: Consider adding analyticsService to the dependency array.

With analyticsService now coming from useServices(), it's technically a dependency of this effect. However, since the service instance is stable (coming from context), the current behavior is safe. The existing eslint-disable comment suggests this was intentional.

Also, the init() wrapper function isn't needed since analyticsService.track() isn't async:

🔎 Optional simplification
   useEffect(() => {
-    async function init() {
-      analyticsService.track("export_certificate", {
-        category: "certificates",
-        label: "Export certificate"
-      });
-    }
-
-    init();
+    analyticsService.track("export_certificate", {
+      category: "certificates",
+      label: "Export certificate"
+    });
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
apps/deploy-web/src/services/stripe/stripe.service.ts (1)

26-30: Type and runtime check are inconsistent.

The type declares publishableKey: string (always present), yet the runtime guard checks for a falsy value. This inconsistency exists because the container uses a non-null assertion. Consider aligning the type with runtime expectations:

🔎 Option: Reflect optional nature in type
 export interface StripeServiceDependencies {
   loadStripe?: typeof loadStripe;
   config: {
-    publishableKey: string;
+    publishableKey: string | undefined;
   };
 }

This would make the runtime guard type-correct and clarify to callers that an undefined key is acceptable.

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

7-7: Use StripeService.name for the root describe block.

As per coding guidelines, use StripeService.name instead of the hardcoded string to enable automated refactoring tools to find all references.

🔎 Suggested fix
-describe("StripeService", () => {
+describe(StripeService.name, () => {
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx (1)

378-378: Double type assertion suggests type mismatch.

The as unknown as typeof TransactionMessageData pattern works but indicates a type compatibility issue between the mock and the actual type. Consider using jest-mock-extended's mock<typeof TransactionMessageData>() for cleaner typing, though the current approach is functional.

🔎 Optional: Cleaner mock typing approach

Consider this alternative at the mock definition (around line 344):

-    const mockTransactionMessageData = {
-      prototype: {},
-      getRevokeCertificateMsg: jest.fn(),
-      getCreateCertificateMsg: jest.fn(),
-      getCreateLeaseMsg: jest.fn(),
-      getCreateDeploymentMsg: jest.fn(),
-      getUpdateDeploymentMsg: jest.fn(),
-      getDepositDeploymentMsg: jest.fn(),
-      getCloseDeploymentMsg: jest.fn(),
-      getSendTokensMsg: jest.fn(),
-      getGrantMsg: jest.fn(),
-      getRevokeDepositMsg: jest.fn(),
-      getGrantBasicAllowanceMsg: jest.fn(),
-      getRevokeAllowanceMsg: jest.fn(),
-      getUpdateProviderMsg: jest.fn()
-    };
+    const mockTransactionMessageData = {
+      getRevokeCertificateMsg: jest.fn(),
+      getCreateCertificateMsg: jest.fn(),
+      getCreateLeaseMsg: jest.fn(),
+      getCreateDeploymentMsg: jest.fn(),
+      getUpdateDeploymentMsg: jest.fn(),
+      getDepositDeploymentMsg: jest.fn(),
+      getCloseDeploymentMsg: jest.fn(),
+      getSendTokensMsg: jest.fn(),
+      getGrantMsg: jest.fn(),
+      getRevokeDepositMsg: jest.fn(),
+      getGrantBasicAllowanceMsg: jest.fn(),
+      getRevokeAllowanceMsg: jest.fn(),
+      getUpdateProviderMsg: jest.fn()
+    } as typeof TransactionMessageData;

Then simplify line 378:

-      TransactionMessageData: mockTransactionMessageData as unknown as typeof TransactionMessageData
+      TransactionMessageData: mockTransactionMessageData
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d155c4 and feac91b.

📒 Files selected for processing (33)
  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/services/stripe/stripe.service.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.
Never use deprecated methods from libraries.
Don't add unnecessary comments to the code.

Files:

  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.ts
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test

Use setup function instead of beforeEach in test files. The setup function must be at the bottom of the root describe block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

**/*.spec.{ts,tsx}: Use <Subject>.name in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references
Use either a method name or a condition starting with 'when' for nested suite descriptions in tests
Use present simple, 3rd person singular for test descriptions without prepending 'should'

Files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
{apps/deploy-web,apps/provider-console}/**/*.spec.tsx

📄 CodeRabbit inference engine (.cursor/rules/query-by-in-tests.mdc)

Use queryBy methods instead of getBy methods in test expectations

Files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: stalniy
Repo: akash-network/console PR: 2255
File: apps/api/src/middlewares/privateMiddleware.ts:5-9
Timestamp: 2025-11-19T16:13:43.249Z
Learning: In the Akash Console API (apps/api), avoid resolving DI container dependencies at module scope (module initialization time) to prevent side effects. Instead, resolve dependencies inside functions/methods where they are actually used, even if this means resolving on every invocation, to maintain explicit control over when side effects occur and improve testability.
📚 Learning: 2025-06-19T16:00:05.428Z
Learnt from: ygrishajev
Repo: akash-network/console PR: 1512
File: apps/deploy-web/src/components/deployments/DeploymentBalanceAlert/DeploymentBalanceAlert.tsx:47-68
Timestamp: 2025-06-19T16:00:05.428Z
Learning: In React Hook Form setups with zod validation, child components using useFormContext() can rely on parent form validation rather than implementing local input validation. Invalid inputs like NaN from parseFloat() are handled by the parent schema validation, eliminating the need for additional local validation in child components.

Applied to files:

  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
📚 Learning: 2025-11-19T15:15:07.283Z
Learnt from: ygrishajev
Repo: akash-network/console PR: 2254
File: apps/api/test/functional/sign-and-broadcast-tx.spec.ts:4-4
Timestamp: 2025-11-19T15:15:07.283Z
Learning: In the Akash Network Console project, when tests use native Node.js fetch (available in Node 18+), fetch-mock should be used for HTTP mocking instead of nock, as nock does not support intercepting native fetch calls. This applies to apps/api/test/functional/sign-and-broadcast-tx.spec.ts and any other tests using native fetch.

Applied to files:

  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
📚 Learning: 2025-05-28T20:42:58.200Z
Learnt from: jzsfkzm
Repo: akash-network/console PR: 1372
File: apps/api/src/dashboard/services/stats/stats.service.ts:0-0
Timestamp: 2025-05-28T20:42:58.200Z
Learning: The user successfully implemented CosmosHttpService with retry logic using axiosRetry, exponential backoff, and proper error handling for Cosmos API calls, replacing direct axios usage in StatsService.

Applied to files:

  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
📚 Learning: 2025-10-15T16:39:55.348Z
Learnt from: jzsfkzm
Repo: akash-network/console PR: 2039
File: apps/deploy-web/tests/ui/change-wallets.spec.ts:4-10
Timestamp: 2025-10-15T16:39:55.348Z
Learning: In the Akash Console E2E tests using the context-with-extension fixture, the first wallet is automatically created during fixture setup via `importWalletToLeap` in `apps/deploy-web/tests/ui/fixture/wallet-setup.ts`, so tests that call `frontPage.createWallet()` are creating a second wallet to test wallet switching functionality.

Applied to files:

  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
📚 Learning: 2025-11-25T17:45:44.790Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/no-jest-mock.mdc:0-0
Timestamp: 2025-11-25T17:45:44.790Z
Learning: Applies to **/*.spec.{ts,tsx} : Don't use `jest.mock()` in test files. Instead, use `jest-mock-extended` to create mocks and pass mocks as dependencies to the service under test

Applied to files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
📚 Learning: 2025-11-25T17:45:49.180Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/query-by-in-tests.mdc:0-0
Timestamp: 2025-11-25T17:45:49.180Z
Learning: Applies to {apps/deploy-web,apps/provider-console}/**/*.spec.tsx : Use `queryBy` methods instead of `getBy` methods in test expectations

Applied to files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
📚 Learning: 2025-11-25T17:45:58.258Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/test-descriptions.mdc:0-0
Timestamp: 2025-11-25T17:45:58.258Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `<Subject>.name` in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
📚 Learning: 2025-11-25T17:45:52.965Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-11-25T17:45:52.965Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `setup` function instead of `beforeEach` in test files. The `setup` function must be at the bottom of the root `describe` block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-06-05T21:07:51.985Z
Learnt from: baktun14
Repo: akash-network/console PR: 1432
File: apps/deploy-web/src/components/deployments/DeploymentAlerts/DeploymentCloseAlert.tsx:38-38
Timestamp: 2025-06-05T21:07:51.985Z
Learning: The ContactPointSelect component in apps/deploy-web/src/components/alerts/ContactPointSelectForm/ContactPointSelect.tsx uses the useFormContext hook internally to connect to React Hook Form, so it doesn't need to be wrapped in a FormField component.

Applied to files:

  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
🧬 Code graph analysis (30)
apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/hooks/useStoredAnonymousUser.ts (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/user/UserFavorites.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/user/AddFundsLink.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/deployments/DeploymentListRow.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/hooks/useTrialBalance.ts (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/user/UserProfileLayout.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/liquidity-modal/index.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/pages/user/api-keys/index.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/user/UserProfile.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/services/app-di-container/browser-di-container.ts (1)
apps/deploy-web/src/services/app-di-container/server-di-container.service.ts (1)
  • services (24-54)
apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/settings/ExportCertificate.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/templates/UserTemplate.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/authorizations/AllowanceModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/authorizations/GrantModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (1)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/services/app-di-container/app-di-container.ts (3)
apps/deploy-web/src/config/browser-env.config.ts (1)
  • browserEnvConfig (4-41)
apps/deploy-web/src/services/stripe/stripe.service.ts (1)
  • StripeService (10-44)
apps/deploy-web/src/services/analytics/analytics.service.ts (1)
  • AnalyticsService (142-271)
apps/deploy-web/src/components/new-deployment/TemplateList.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/sdl/ImportSdlModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/services/stripe/stripe.service.spec.ts (1)
apps/deploy-web/src/services/stripe/stripe.service.ts (2)
  • StripeServiceDependencies (3-8)
  • StripeService (10-44)
apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx (1)
apps/deploy-web/src/utils/TransactionMessageData.ts (1)
  • TransactionMessageData (12-234)
apps/deploy-web/src/services/analytics/analytics.service.ts (2)
apps/api/src/core/providers/amplitude.provider.ts (1)
  • Amplitude (16-20)
apps/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx (1)
  • GoogleAnalytics (7-17)
🪛 Biome (2.1.2)
apps/deploy-web/src/services/analytics/analytics.service.ts

[error] 155-155: This parameter is used before its declaration.

The parameter is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (40)
apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx (2)

2-2: LGTM! Proper use of jest-mock-extended for analytics service mocking.

The imports correctly use jest-mock-extended for creating mocks and import AnalyticsService as a type-only import, which aligns with TypeScript best practices and the coding guidelines.

Also applies to: 6-6


114-114: LGTM! Correct implementation of analyticsService mock injection.

The addition of analyticsService: () => mock<AnalyticsService>() to the TestContainerProvider services properly implements the DI pattern for analytics service in tests. This follows the established pattern used for other services and ensures test isolation by creating a fresh mock for each test.

apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx (2)

15-15: LGTM! Correct implementation of DI pattern.

The change correctly moves appConfig resolution from module scope into the component, accessing it via useServices() from the DI container. The alias publicConfig: appConfig maintains compatibility with the existing code below.

Based on learnings, this approach avoids uncontrolled side effects at module initialization time and improves testability.


31-47: LGTM! Services correctly injected via DI container.

The UserTracker component properly retrieves analyticsService and userTracker from the DI container and includes them in the useEffect dependency array (line 44). This ensures the effect re-runs when services change and aligns with the PR's objective of moving analyticsService to the DI container.

apps/deploy-web/src/components/user/AddFundsLink.tsx (1)

6-6: LGTM! Clean migration to context-provided analytics service.

The change correctly moves analyticsService resolution from module scope to inside the component using useServices(), which aligns with the DI best practices. This improves testability and reduces uncontrolled side effects.

Based on learnings, resolving dependencies inside components rather than at module scope maintains explicit control over side effects.

Also applies to: 17-17, 28-28

apps/deploy-web/src/components/sdl/RentGpusForm.tsx (1)

49-49: LGTM! Configuration source updated correctly.

The change from appConfig to publicConfig: appConfig aligns with the container refactoring. The aliasing maintains compatibility with existing code while centralizing configuration access through the DI container.

apps/deploy-web/src/hooks/useStoredAnonymousUser.ts (1)

19-19: LGTM! Hook correctly updated to use publicConfig.

The change maintains the same functionality while sourcing configuration from the centralized DI container. The aliasing preserves compatibility with existing usage.

apps/deploy-web/src/hooks/useTrialBalance.ts (1)

20-20: LGTM! Configuration access properly centralized.

The hook correctly accesses trial configuration through the DI container's publicConfig, maintaining existing functionality while following the refactored pattern.

apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx (1)

8-8: LGTM! Analytics service properly injected via context.

The migration correctly moves analytics tracking to use the context-provided service, improving testability and reducing module-level side effects.

Also applies to: 33-33, 51-54

apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx (1)

12-12: LGTM! Multiple analytics tracking points successfully migrated.

All analytics tracking calls now use the context-provided service. The refactoring maintains existing behavior while properly centralizing service access through the DI container.

Also applies to: 51-51, 70-70, 87-87

apps/deploy-web/src/components/user/UserFavorites.tsx (1)

6-6: LGTM! Analytics tracking correctly refactored.

The component properly retrieves analyticsService from the DI container and uses it for user profile interaction tracking, following the established pattern.

Also applies to: 12-12, 38-41

apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx (1)

10-10: LGTM! API key creation analytics properly migrated.

The modal correctly uses the context-provided analytics service for tracking API key creation, completing the consistent pattern of DI-based service access across the codebase.

Also applies to: 30-30, 89-92

apps/deploy-web/src/components/new-deployment/TemplateList.tsx (1)

11-11: LGTM!

The refactor correctly moves analyticsService from a static module import to the DI container via useServices(). This aligns with the retrieved learning about avoiding module-scope DI resolution to prevent uncontrolled side effects and improve testability.

Also applies to: 47-47

apps/deploy-web/src/components/deployments/DeploymentListRow.tsx (1)

24-24: LGTM!

The analyticsService is now correctly obtained from the DI container via useServices(). The analytics tracking calls remain unchanged, and the refactor properly aligns with the pattern of resolving dependencies at runtime rather than module initialization.

Also applies to: 63-63

apps/deploy-web/src/components/authorizations/AllowanceModal.tsx (1)

13-13: LGTM!

The refactor correctly obtains analyticsService from the DI container. The tracking call in onSubmit remains unchanged in behavior.

Also applies to: 35-35

apps/deploy-web/src/components/authorizations/GrantModal.tsx (1)

26-26: LGTM!

The refactor correctly obtains analyticsService from the DI container via useServices().

Also applies to: 55-55

apps/deploy-web/src/services/analytics/analytics.service.ts (1)

153-164: Constructor refactor improves testability.

The defaulted public parameters pattern is a good approach for DI - it allows the service to work out-of-the-box while enabling test mocks to be injected. The isBrowser guards for window.gtag and localStorage are appropriate for SSR compatibility.

apps/deploy-web/src/components/templates/UserTemplate.tsx (1)

15-15: LGTM!

The refactor correctly obtains analyticsService from the DI container. All analytics tracking calls throughout the component maintain their existing behavior.

Also applies to: 34-34

apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx (1)

75-75: LGTM! Service dependencies correctly injected via context.

The refactoring moves analyticsService and configuration resolution from module scope to component scope via useServices(), aligning with best practices for dependency injection. The publicConfig aliasing to appConfig maintains backward compatibility within the component.

Based on learnings, this approach avoids module-level side effects and improves testability by making dependencies explicit.

apps/deploy-web/src/components/sdl/ImportSdlModal.tsx (1)

12-12: LGTM! Analytics service correctly injected via DI container.

The refactoring replaces the direct module import with context-provided analyticsService via useServices(), improving testability and avoiding uncontrolled side effects at module scope.

Based on learnings, resolving dependencies inside components rather than at module initialization time provides better control over side effects.

Also applies to: 24-24

apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx (1)

18-18: LGTM! Service dependency properly injected.

The change moves analyticsService resolution from module scope to component scope via the DI container, improving testability and explicit dependency management. All usages occur within event handlers with no dependency array concerns.

Also applies to: 51-51

apps/deploy-web/src/pages/user/api-keys/index.tsx (1)

9-9: LGTM! Analytics service correctly provided via context.

The refactoring consistently applies the DI pattern, moving analyticsService from a static import to context-based injection, which enhances testability.

Also applies to: 13-13

apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx (1)

13-13: LGTM! Dependency injection applied correctly.

The component now obtains analyticsService from the DI container via useServices(), maintaining consistency with the broader refactoring effort and improving testability.

Also applies to: 45-45

apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx (1)

40-40: LGTM! Configuration sourced from publicConfig.

The change updates the configuration source from appConfig to publicConfig (aliased as appConfig) to align with the centralized DI container pattern. This maintains backward compatibility while adopting the new configuration structure.

apps/deploy-web/src/components/user/UserProfileLayout.tsx (2)

7-7: LGTM! Multiple services correctly injected via DI container.

The refactoring moves both analyticsService and urlService from static imports to context-provided instances via useServices(). This improves testability by allowing easy mocking of both services and eliminates module-scope side effects.

Also applies to: 21-21


31-31: LGTM! URL service calls correctly updated.

All UrlService.* static method calls have been replaced with urlService.* instance method calls, consistent with the DI container pattern. The routing logic remains unchanged.

Also applies to: 34-34, 37-37

apps/deploy-web/src/services/app-di-container/browser-di-container.ts (1)

39-39: LGTM! Configuration reference updated to publicConfig.

The DI container correctly references services.publicConfig instead of services.appConfig for the base API URL configuration. This aligns with the broader refactoring to standardize configuration access through publicConfig across the application.

apps/deploy-web/src/services/app-di-container/app-di-container.ts (4)

19-20: LGTM!

Clean imports for the DI container setup. The AnalyticsService is now created within the container rather than imported as a singleton, aligning with the goal of reducing uncontrolled side effects.


36-37: LGTM!

Exposing publicConfig as a factory aligns with the DI pattern and provides a centralized configuration source for other services in the container.


71-76: Non-null assertion may cause silent failures.

The ! operator on NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY suppresses TypeScript checks, but if the env var is missing, the StripeService will receive an empty/undefined value despite the type claiming it's a string. While getStripe() has a runtime guard, the type contract becomes misleading.

Consider removing the assertion and letting the config type reflect reality (string | undefined), or validate at container initialization.

🔎 Suggested fix
     stripeService: () =>
       new StripeService({
         config: {
-          publishableKey: container.publicConfig.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
+          publishableKey: container.publicConfig.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? ""
         }
       }),

Alternatively, update StripeServiceDependencies to accept publishableKey: string | undefined if empty keys are expected.


137-148: LGTM!

The AnalyticsService instantiation properly uses the publicConfig values and follows the DI pattern. This improves testability by making the configuration explicit rather than relying on module-level singletons. Based on learnings, this aligns with the guidance to resolve dependencies inside functions rather than at module scope.

apps/deploy-web/src/components/user/UserProfile.tsx (2)

8-8: LGTM!

Clean migration to the DI pattern. Using useServices() to obtain analyticsService and urlService improves testability and aligns with the broader refactor across the codebase.

Also applies to: 20-20


41-47: LGTM!

Correctly replaced the static UrlService.sdlBuilder() call with the instance method from the context-provided urlService.

apps/deploy-web/src/services/stripe/stripe.service.ts (1)

3-8: LGTM!

Clean dependency interface that explicitly requires a config object with publishableKey, while making loadStripe optional to allow injection of mocks during testing. This improves testability significantly.

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

75-91: LGTM!

The setup function correctly:

  • Sits at the bottom of the root describe block
  • Creates the subject under test with explicit dependencies
  • Accepts a single parameter with inline type definition
  • Avoids shared state
  • Has no specified return type

The test structure aligns with the updated StripeServiceDependencies interface.

apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx (2)

369-369: LGTM! Consistent refactoring pattern.

The same aliasing approach is correctly applied in the nested component, maintaining consistency with the WalletProvider changes.


76-76: Aliasing pattern correctly applied and consistently used throughout codebase.

The publicConfig: appConfig aliasing at line 76 maintains backward compatibility within the component. All destructured services (analyticsService, txHttpService, appConfig, urlService, windowLocation) are actively used. Verification confirms this refactoring pattern has been applied consistently across the codebase with no remaining unaliased appConfig destructurings from useServices().

apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (2)

292-292: LGTM!

Correctly uses urlService from the DI container instead of a static import. This change aligns with the PR's refactoring objectives.


75-84: DI pattern refactoring is correctly implemented.

The destructuring of services from useServices() properly aligns with the PR objective. The publicConfig includes the required NEXT_PUBLIC_DEFAULT_INITIAL_DEPOSIT property (defined in browser-env.config.ts and validated with a default of 500000), so the aliasing to appConfig is safe and follows the established pattern across the codebase.

apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx (1)

285-295: LGTM!

The test mock correctly reflects the production code change where useServices() returns publicConfig (aliased to appConfig in the component). The mock structure properly maintains test coverage.

@stalniy stalniy force-pushed the refactor/analytics-service branch from feac91b to 68e37c6 Compare December 25, 2025 14:36
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

Caution

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

⚠️ Outside diff range comments (2)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (2)

98-122: Add missing dependencies to useEffect dependency array.

The effect uses windowLocation (line 107) and windowHistory (line 120) but doesn't include them in the dependency array on line 122. This violates React's exhaustive-deps rule and could lead to stale closures.

🔎 Proposed fix
-  }, [analyticsService, d.localStorage]);
+  }, [analyticsService, d.localStorage, windowLocation, windowHistory]);

201-316: Add missing urlService to useCallback dependency array.

The callback uses urlService on line 292 (router.push(urlService.newDeployment(...))) but doesn't include it in the dependency array at lines 298-315. This violates React's exhaustive-deps rule and could lead to stale closures.

🔎 Proposed fix
   [
     d,
     router,
+    urlService,
     connectManagedWallet,
     templates,
🧹 Nitpick comments (3)
apps/deploy-web/src/services/app-di-container/app-di-container.ts (1)

71-76: Non-null assertion on environment variable.

The ! assertion on NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY silently assumes the value is always defined. While StripeService.getStripe() handles missing keys gracefully at runtime, consider using an empty string fallback to make the optionality explicit:

🔎 Suggested change
     stripeService: () =>
       new StripeService({
         config: {
-          publishableKey: container.publicConfig.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
+          publishableKey: container.publicConfig.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? ""
         }
       }),
apps/deploy-web/src/services/stripe/stripe.service.spec.ts (1)

8-52: Consider adding a test for missing publishableKey.

The test suite covers the happy path and error handling well. Consider adding a test case for when publishableKey is an empty string to verify the warning behavior in getStripe().

🔎 Example test case
it("returns null and warns when publishable key is empty", async () => {
  const { stripeService, mockLoadStripe } = setup({
    publishableKey: ""
  });
  const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation();

  const result = await stripeService.getStripe();

  expect(result).toBeNull();
  expect(mockLoadStripe).not.toHaveBeenCalled();
  expect(consoleWarnSpy).toHaveBeenCalledWith("Stripe publishable key is not configured");
  consoleWarnSpy.mockRestore();
});
apps/deploy-web/src/hooks/useTrialBalance.ts (1)

22-27: Consider adding defensive defaults for config values.

If publicConfig lacks NEXT_PUBLIC_TRIAL_CREDITS_AMOUNT or it's zero, line 27's division could produce Infinity or NaN, breaking the percentage calculation.

🔎 Optional: Add fallback defaults
-  const TRIAL_TOTAL = appConfig.NEXT_PUBLIC_TRIAL_CREDITS_AMOUNT;
-  const TRIAL_DURATION_DAYS = appConfig.NEXT_PUBLIC_TRIAL_DURATION_DAYS;
+  const TRIAL_TOTAL = appConfig.NEXT_PUBLIC_TRIAL_CREDITS_AMOUNT || 0;
+  const TRIAL_DURATION_DAYS = appConfig.NEXT_PUBLIC_TRIAL_DURATION_DAYS || 0;

Or validate that these values are positive numbers when initializing publicConfig in the DI container.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between feac91b and 68e37c6.

📒 Files selected for processing (33)
  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/services/stripe/stripe.service.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.
Never use deprecated methods from libraries.
Don't add unnecessary comments to the code.

Files:

  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/services/stripe/stripe.service.ts
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test

Use setup function instead of beforeEach in test files. The setup function must be at the bottom of the root describe block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

**/*.spec.{ts,tsx}: Use <Subject>.name in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references
Use either a method name or a condition starting with 'when' for nested suite descriptions in tests
Use present simple, 3rd person singular for test descriptions without prepending 'should'

Files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
{apps/deploy-web,apps/provider-console}/**/*.spec.tsx

📄 CodeRabbit inference engine (.cursor/rules/query-by-in-tests.mdc)

Use queryBy methods instead of getBy methods in test expectations

Files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: stalniy
Repo: akash-network/console PR: 2255
File: apps/api/src/middlewares/privateMiddleware.ts:5-9
Timestamp: 2025-11-19T16:13:43.249Z
Learning: In the Akash Console API (apps/api), avoid resolving DI container dependencies at module scope (module initialization time) to prevent side effects. Instead, resolve dependencies inside functions/methods where they are actually used, even if this means resolving on every invocation, to maintain explicit control over when side effects occur and improve testability.
📚 Learning: 2025-11-25T17:45:58.258Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/test-descriptions.mdc:0-0
Timestamp: 2025-11-25T17:45:58.258Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `<Subject>.name` in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-11-25T17:45:44.790Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/no-jest-mock.mdc:0-0
Timestamp: 2025-11-25T17:45:44.790Z
Learning: Applies to **/*.spec.{ts,tsx} : Don't use `jest.mock()` in test files. Instead, use `jest-mock-extended` to create mocks and pass mocks as dependencies to the service under test

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
📚 Learning: 2025-11-25T17:45:52.965Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-11-25T17:45:52.965Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `setup` function instead of `beforeEach` in test files. The `setup` function must be at the bottom of the root `describe` block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-11-25T17:45:49.180Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/query-by-in-tests.mdc:0-0
Timestamp: 2025-11-25T17:45:49.180Z
Learning: Applies to {apps/deploy-web,apps/provider-console}/**/*.spec.tsx : Use `queryBy` methods instead of `getBy` methods in test expectations

Applied to files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
📚 Learning: 2025-11-19T15:15:07.283Z
Learnt from: ygrishajev
Repo: akash-network/console PR: 2254
File: apps/api/test/functional/sign-and-broadcast-tx.spec.ts:4-4
Timestamp: 2025-11-19T15:15:07.283Z
Learning: In the Akash Network Console project, when tests use native Node.js fetch (available in Node 18+), fetch-mock should be used for HTTP mocking instead of nock, as nock does not support intercepting native fetch calls. This applies to apps/api/test/functional/sign-and-broadcast-tx.spec.ts and any other tests using native fetch.

Applied to files:

  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
📚 Learning: 2025-06-19T16:00:05.428Z
Learnt from: ygrishajev
Repo: akash-network/console PR: 1512
File: apps/deploy-web/src/components/deployments/DeploymentBalanceAlert/DeploymentBalanceAlert.tsx:47-68
Timestamp: 2025-06-19T16:00:05.428Z
Learning: In React Hook Form setups with zod validation, child components using useFormContext() can rely on parent form validation rather than implementing local input validation. Invalid inputs like NaN from parseFloat() are handled by the parent schema validation, eliminating the need for additional local validation in child components.

Applied to files:

  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
🧬 Code graph analysis (9)
apps/deploy-web/src/components/sdl/ImportSdlModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/services/stripe/stripe.service.spec.ts (1)
apps/deploy-web/src/services/stripe/stripe.service.ts (2)
  • StripeServiceDependencies (3-8)
  • StripeService (10-44)
apps/deploy-web/src/components/user/AddFundsLink.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (1)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx (2)
apps/deploy-web/tests/unit/TestContainerProvider.tsx (1)
  • TestContainerProvider (9-32)
apps/deploy-web/src/services/analytics/analytics.service.ts (1)
  • AnalyticsService (144-273)
apps/deploy-web/src/components/user/UserProfile.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/templates/UserTemplate.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (26)
apps/deploy-web/src/components/sdl/ImportSdlModal.tsx (1)

12-12: LGTM! Clean refactor to DI-based service access.

The change correctly replaces the direct analyticsService import with context-based access via useServices(). The hook is properly called at the component's top level, following React rules, and the DI resolution occurs inside the component function rather than at module scope, which aligns with the learnings.

Also applies to: 24-24

apps/deploy-web/src/components/user/UserProfile.tsx (2)

8-8: LGTM! Excellent refactor aligning with DI best practices.

The migration to context-based service resolution is well-executed:

  • Services are resolved inside the component function rather than at module scope, avoiding initialization-time side effects.
  • Follows React hooks rules by calling useServices() at the top level.
  • Maintains existing functionality while improving testability.

Based on learnings, this approach correctly defers DI container resolution to function execution time rather than module initialization.

Also applies to: 20-20


41-41: Service instance correctly used.

The shift from static UrlService.sdlBuilder() to instance urlService.sdlBuilder() is consistent with the DI container pattern.

apps/deploy-web/src/components/templates/UserTemplate.tsx (2)

15-15: LGTM! DI resolution follows project guidelines.

The refactoring correctly moves analyticsService from a static import to DI-provided service via useServices(). The resolution happens inside the component function rather than at module scope, which improves testability and explicit control over side effects.

Based on learnings, this approach aligns with the project's preference to resolve DI container dependencies inside functions where they're actually used rather than at module initialization time.

Also applies to: 34-34


54-57: LGTM! Analytics tracking usage is consistent.

All analyticsService.track() calls maintain consistent usage patterns with appropriate event names, categories, and labels for each user action. The refactoring doesn't alter the tracking logic, only the source of the service instance.

Also applies to: 70-73, 114-117, 127-130, 153-156, 166-169, 172-175

apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx (3)

8-8: LGTM!

Clean import addition that enables access to the DI container services.


33-33: LGTM! Follows DI best practices.

The analyticsService is correctly resolved inside the component function rather than at module scope, which maintains explicit control over side effects and improves testability as intended by this refactor.

Based on learnings, this approach properly avoids resolving DI container dependencies at module initialization time.


48-57: LGTM! Analytics tracking properly integrated.

The analytics tracking is well-placed in the onSubmit handler and correctly uses the DI-provided analyticsService. The fire-and-forget pattern (no await) is appropriate for analytics, as it tracks user intent without blocking the UI flow.

apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (4)

47-64: LGTM! DEPENDENCIES object correctly updated.

The removal of UrlService from the DEPENDENCIES object is consistent with the refactoring to access it via the DI container through useServices().


75-84: LGTM! Services correctly accessed via DI container.

The destructuring of services from useServices() follows the DI pattern and aligns with the PR objectives. The aliasing of publicConfig as appConfig maintains internal consistency within the component.

Based on learnings, this correctly resolves DI dependencies inside the component rather than at module scope.


181-181: LGTM! Correctly using urlService from DI container.

The usage of urlService from the DI container (instead of the previous d.UrlService) is correct and consistent with the refactoring objectives. The dependency array at line 183 properly includes urlService.


292-292: LGTM! Correctly using urlService from DI container.

The usage of urlService from the DI container (instead of the previous d.UrlService) is correct and consistent with the refactoring objectives.

apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx (2)

2-2: LGTM: Correct use of jest-mock-extended for analytics mocking.

The imports follow the project's testing guidelines by using jest-mock-extended instead of jest.mock(), and the type-only import of AnalyticsService is appropriately scoped.

Also applies to: 6-6


114-114: LGTM: Analytics service correctly injected via DI.

The mocked analyticsService is properly provided through the test container using the factory pattern, aligning with the PR's objective to move analytics service creation to the DI container.

Note: If future tests need to verify analytics event tracking, consider storing the mock in a variable to enable assertions:

const analyticsServiceMock = mock<AnalyticsService>();
// ... later in services
analyticsService: () => analyticsServiceMock
apps/deploy-web/src/services/stripe/stripe.service.ts (3)

3-8: LGTM!

The interface redesign cleanly separates the optional dependency (loadStripe) from the required configuration (config.publishableKey), making the service more testable and explicit about its configuration requirements.


10-19: LGTM!

The constructor correctly provides a default for loadStripe while allowing injection for testing. The spread order ensures provided dependencies override defaults.


21-39: LGTM!

The method correctly retrieves the publishable key from the new config path while maintaining defensive checks for missing configuration.

apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx (1)

10-10: LGTM!

Migrating analyticsService from a direct import to useServices() properly aligns with the DI container pattern, improving testability by making the dependency injectable. Based on retrieved learnings, this avoids resolving dependencies at module scope.

Also applies to: 29-30

apps/deploy-web/src/services/app-di-container/app-di-container.ts (2)

36-36: LGTM!

Exposing browserEnvConfig through the DI container as publicConfig centralizes configuration access and maintains the lazy resolution pattern.


137-148: LGTM!

The AnalyticsService factory properly centralizes configuration and follows the lazy resolution pattern. This aligns with the PR objective to move analytics service creation into the DI container, reducing uncontrolled side effects and improving testability. Based on retrieved learnings, resolving dependencies inside factory functions is the correct approach.

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

75-91: LGTM!

The setup function correctly mirrors the updated StripeService constructor signature, passing the config object with publishableKey. The type annotation update to Required<StripeServiceDependencies>["loadStripe"] accurately reflects that loadStripe becomes required after the service is constructed.

apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx (2)

15-15: LGTM! Clean aliasing pattern.

The destructuring correctly aliases publicConfig to appConfig for local use, maintaining consistency within the component while adapting to the updated DI container structure.


31-47: Service stability is properly managed through memoization.

The DI container is memoized in ServicesProvider (line 25 via useMemo) with dependencies on settings changes, ensuring analyticsService and userTracker are stable references across renders. The effect dependency array correctly includes these services, which will only change when settings change, making the implementation safe and aligned with best practices.

The UserTracker component correctly resolves dependencies at component scope (not module scope), improving testability and avoiding unintended side effects.

apps/deploy-web/src/hooks/useTrialBalance.ts (1)

20-20: Good refactor to use DI-provided config.

This change correctly sources configuration from the DI container's publicConfig, aligning with the PR's objective. The aliasing pattern maintains backward compatibility with downstream code. The dependency is resolved inside the hook function rather than at module scope, which follows best practices for DI container usage.

apps/deploy-web/src/components/user/AddFundsLink.tsx (2)

6-6: LGTM! Proper DI container integration.

The migration from direct analyticsService import to context-provided service via useServices() is correctly implemented. The hook is invoked at the component level (not module scope), which is the appropriate pattern for React components and aligns with the PR's objective to improve testability.

Also applies to: 17-17


28-28: The track method already uses proper TypeScript typing with the AnalyticsEvent union type, not a generic string type. The event "add_funds_btn_clk" is a valid member of that union. No type safety issues exist, and the code follows all coding guidelines.

@stalniy stalniy force-pushed the refactor/analytics-service branch from 68e37c6 to b3f66bc Compare December 29, 2025 12: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: 0

Caution

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

⚠️ Outside diff range comments (4)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (2)

98-122: Add missing dependencies to useEffect

The useEffect uses windowLocation (lines 107-108, 118) and windowHistory (line 120) but they are not included in the dependency array at line 122. This violates React's exhaustive-deps rule and could lead to stale closure bugs.

🔎 Proposed fix
-  }, [analyticsService, d.localStorage]);
+  }, [analyticsService, d.localStorage, windowLocation, windowHistory]);

201-316: Add missing urlService dependency to useCallback

The handleComplete callback uses urlService on line 292 but it's not included in the dependency array (lines 298-315). This violates React's exhaustive-deps rule and creates an inconsistency with how other services like analyticsService, chainApiHttpClient, and deploymentLocalStorage are properly included.

🔎 Proposed fix
     [
       d,
       router,
       connectManagedWallet,
       templates,
       chainApiHttpClient,
       address,
       appConfig,
       depositParams,
       genNewCertificateIfLocalIsInvalid,
       signAndBroadcastTx,
       updateSelectedCertificate,
       deploymentLocalStorage,
       analyticsService,
       enqueueSnackbar,
       errorHandler,
-      managedDenom
+      managedDenom,
+      urlService
     ]
apps/deploy-web/src/services/stripe/stripe.service.spec.ts (1)

7-7: Use class name reference instead of hardcoded string.

The root describe should use StripeService.name instead of the hardcoded string "StripeService" to enable automated refactoring tools to find all references.

As per coding guidelines for test files.

🔎 Proposed fix
-describe("StripeService", () => {
+describe(StripeService.name, () => {
apps/deploy-web/src/services/analytics/analytics.service.ts (1)

210-218: Potential race condition in ensureAmplitudeFor.

Multiple concurrent calls to identify() could all pass the typeof this.isAmplitudeEnabled === "undefined" check before any of them sets the value, potentially resulting in amplitudeClient.init() being called multiple times with the same API key.

While calling init() multiple times may not cause data corruption, it could lead to unexpected behavior or duplicate event tracking. Consider using a promise-based initialization pattern or ensuring single initialization.

Based on learnings about race conditions in async initialization patterns.

🔎 Potential fix using promise caching
 export class AnalyticsService {
   private readonly STORAGE_KEY = "analytics_values_cache";
 
   private readonly valuesCache: Map<string, string> = this.loadSwitchValuesFromStorage();
 
   private isAmplitudeEnabled: boolean | undefined;
+  private amplitudeInitPromise: Promise<void> | undefined;
 
   private get gtag() {
     return this.getGtag();
   }

   private ensureAmplitudeFor(user: AnalyticsUser) {
     if (typeof this.isAmplitudeEnabled === "undefined" && user.id) {
+      if (!this.amplitudeInitPromise) {
+        this.amplitudeInitPromise = this.initializeAmplitude(user.id);
+      }
+      return this.amplitudeInitPromise;
+    }
+  }
+
+  private async initializeAmplitude(userId: string): Promise<void> {
-      this.isAmplitudeEnabled = this.shouldSampleUser(user.id);
+      this.isAmplitudeEnabled = this.shouldSampleUser(userId);
 
       if (this.isAmplitudeEnabled) {
         this.amplitudeClient.init(this.options.amplitude.apiKey);
       }
-    }
   }

Note: This would require updating identify() to handle the promise if you want to await initialization.

🧹 Nitpick comments (2)
apps/deploy-web/src/components/settings/ExportCertificate.tsx (1)

13-23: Consider simplifying the effect.

The async init() wrapper function doesn't use await, making it unnecessary. You could simplify this to directly call the tracking method.

🔎 Proposed simplification
  useEffect(() => {
-   async function init() {
-     analyticsService.track("export_certificate", {
-       category: "certificates",
-       label: "Export certificate"
-     });
-   }
-
-   init();
+   analyticsService.track("export_certificate", {
+     category: "certificates",
+     label: "Export certificate"
+   });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
apps/deploy-web/src/services/stripe/stripe.service.ts (1)

26-30: Consider aligning type with runtime check.

The runtime check on line 27 handles falsy publishableKey values, but the interface type declares it as a required string. For type consistency, consider either:

  • Make the type optional: publishableKey?: string (if it can genuinely be absent)
  • Or keep the defensive check as-is (valid defensive coding even with strong types)
🔎 Optional type refinement
 export interface StripeServiceDependencies {
   loadStripe?: typeof loadStripe;
   config: {
-    publishableKey: string;
+    publishableKey?: string;
   };
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68e37c6 and b3f66bc.

📒 Files selected for processing (33)
  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/services/stripe/stripe.service.ts
🚧 Files skipped from review as they are similar to previous changes (21)
  • apps/deploy-web/src/components/user/UserFavorites.tsx
  • apps/deploy-web/src/components/user/UserProviders/UserProviders.tsx
  • apps/deploy-web/src/components/user/AddFundsLink.tsx
  • apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
  • apps/deploy-web/src/components/new-deployment/BidRow/BidRow.spec.tsx
  • apps/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx
  • apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
  • apps/deploy-web/src/components/liquidity-modal/index.tsx
  • apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
  • apps/deploy-web/src/components/user/UserProfile.tsx
  • apps/deploy-web/src/hooks/useTrialBalance.ts
  • apps/deploy-web/src/components/user/UserProfileLayout.tsx
  • apps/deploy-web/src/components/sdl/ImportSdlModal.tsx
  • apps/deploy-web/src/components/authorizations/GrantModal.tsx
  • apps/deploy-web/src/components/authorizations/AllowanceModal.tsx
  • apps/deploy-web/src/components/sdl/SaveTemplateModal.tsx
  • apps/deploy-web/src/components/api-keys/CreateApiKeyModal.tsx
  • apps/deploy-web/src/hooks/useStoredAnonymousUser.ts
  • apps/deploy-web/src/components/deployments/ShellDownloadModal.tsx
  • apps/deploy-web/src/context/WalletProvider/WalletProvider.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{ts,tsx,js}: Never use type any or cast to type any. Always define the proper TypeScript types.
Never use deprecated methods from libraries.
Don't add unnecessary comments to the code.

Files:

  • apps/deploy-web/src/components/settings/ExportCertificate.tsx
  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
  • apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx
  • apps/deploy-web/src/components/new-deployment/TemplateList.tsx
  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
  • apps/deploy-web/src/services/analytics/analytics.service.ts
  • apps/deploy-web/src/pages/user/api-keys/index.tsx
  • apps/deploy-web/src/services/app-di-container/app-di-container.ts
  • apps/deploy-web/src/services/stripe/stripe.service.ts
  • apps/deploy-web/src/components/templates/UserTemplate.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test

Use setup function instead of beforeEach in test files. The setup function must be at the bottom of the root describe block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

**/*.spec.{ts,tsx}: Use <Subject>.name in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references
Use either a method name or a condition starting with 'when' for nested suite descriptions in tests
Use present simple, 3rd person singular for test descriptions without prepending 'should'

Files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: stalniy
Repo: akash-network/console PR: 2255
File: apps/api/src/middlewares/privateMiddleware.ts:5-9
Timestamp: 2025-11-19T16:13:43.249Z
Learning: In the Akash Console API (apps/api), avoid resolving DI container dependencies at module scope (module initialization time) to prevent side effects. Instead, resolve dependencies inside functions/methods where they are actually used, even if this means resolving on every invocation, to maintain explicit control over when side effects occur and improve testability.
📚 Learning: 2025-11-19T15:15:07.283Z
Learnt from: ygrishajev
Repo: akash-network/console PR: 2254
File: apps/api/test/functional/sign-and-broadcast-tx.spec.ts:4-4
Timestamp: 2025-11-19T15:15:07.283Z
Learning: In the Akash Network Console project, when tests use native Node.js fetch (available in Node 18+), fetch-mock should be used for HTTP mocking instead of nock, as nock does not support intercepting native fetch calls. This applies to apps/api/test/functional/sign-and-broadcast-tx.spec.ts and any other tests using native fetch.

Applied to files:

  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
📚 Learning: 2025-05-28T20:42:58.200Z
Learnt from: jzsfkzm
Repo: akash-network/console PR: 1372
File: apps/api/src/dashboard/services/stats/stats.service.ts:0-0
Timestamp: 2025-05-28T20:42:58.200Z
Learning: The user successfully implemented CosmosHttpService with retry logic using axiosRetry, exponential backoff, and proper error handling for Cosmos API calls, replacing direct axios usage in StatsService.

Applied to files:

  • apps/deploy-web/src/services/app-di-container/browser-di-container.ts
📚 Learning: 2025-11-25T17:45:44.790Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/no-jest-mock.mdc:0-0
Timestamp: 2025-11-25T17:45:44.790Z
Learning: Applies to **/*.spec.{ts,tsx} : Don't use `jest.mock()` in test files. Instead, use `jest-mock-extended` to create mocks and pass mocks as dependencies to the service under test

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-11-25T17:45:58.258Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/test-descriptions.mdc:0-0
Timestamp: 2025-11-25T17:45:58.258Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `<Subject>.name` in the root describe suite description instead of hardcoded class/service name strings to enable automated refactoring tools to find all references

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-11-25T17:45:52.965Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-11-25T17:45:52.965Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `setup` function instead of `beforeEach` in test files. The `setup` function must be at the bottom of the root `describe` block, should create an object under test and return it, accept a single parameter with inline type definition, avoid shared state, and not have a specified return type.

Applied to files:

  • apps/deploy-web/src/services/stripe/stripe.service.spec.ts
📚 Learning: 2025-06-05T21:07:51.985Z
Learnt from: baktun14
Repo: akash-network/console PR: 1432
File: apps/deploy-web/src/components/deployments/DeploymentAlerts/DeploymentCloseAlert.tsx:38-38
Timestamp: 2025-06-05T21:07:51.985Z
Learning: The ContactPointSelect component in apps/deploy-web/src/components/alerts/ContactPointSelectForm/ContactPointSelect.tsx uses the useFormContext hook internally to connect to React Hook Form, so it doesn't need to be wrapped in a FormField component.

Applied to files:

  • apps/deploy-web/src/components/sdl/RentGpusForm.tsx
📚 Learning: 2025-11-25T17:45:39.561Z
Learnt from: CR
Repo: akash-network/console PR: 0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-11-25T17:45:39.561Z
Learning: Applies to **/*.{ts,tsx,js} : Never use deprecated methods from libraries.

Applied to files:

  • apps/deploy-web/src/services/analytics/analytics.service.ts
📚 Learning: 2025-10-31T11:26:42.138Z
Learnt from: stalniy
Repo: akash-network/console PR: 2138
File: apps/api/src/billing/lib/batch-signing-client/batch-signing-client.service.ts:379-382
Timestamp: 2025-10-31T11:26:42.138Z
Learning: In TypeScript/JavaScript, the pattern of checking a cached value and then performing an async operation to fetch it without proper synchronization is race condition unsafe:
```typescript
private async getAddress() {
  if (!this.address) {
    this.address = await this.wallet.getFirstAddress();
  }
  return this.address;
}
```
Multiple concurrent calls can all pass the `if (!this.address)` check before any of them sets the value, leading to duplicate async operations. This should be flagged as a race condition. Proper synchronization (mutex, atomic promise caching, or guaranteed single-threaded execution) is required.

Applied to files:

  • apps/deploy-web/src/services/analytics/analytics.service.ts
🧬 Code graph analysis (10)
apps/deploy-web/src/components/settings/ExportCertificate.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/services/app-di-container/browser-di-container.ts (1)
apps/deploy-web/src/services/app-di-container/server-di-container.service.ts (1)
  • services (24-54)
apps/deploy-web/src/services/stripe/stripe.service.spec.ts (1)
apps/deploy-web/src/services/stripe/stripe.service.ts (2)
  • StripeServiceDependencies (3-8)
  • StripeService (10-44)
apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/new-deployment/TemplateList.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/sdl/RentGpusForm.tsx (2)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/pages/user/api-keys/index.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/services/app-di-container/app-di-container.ts (4)
apps/deploy-web/src/config/browser-env.config.ts (1)
  • browserEnvConfig (4-41)
apps/deploy-web/src/services/stripe/stripe.service.ts (1)
  • StripeService (10-44)
packages/http-sdk/src/stripe/stripe.service.ts (1)
  • StripeService (19-103)
apps/deploy-web/src/services/analytics/analytics.service.ts (1)
  • AnalyticsService (144-273)
apps/deploy-web/src/components/templates/UserTemplate.tsx (1)
apps/deploy-web/src/context/ServicesProvider/ServicesProvider.tsx (1)
  • useServices (33-35)
apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (1)
apps/api/src/billing/config/index.ts (1)
  • appConfig (3-5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (17)
apps/deploy-web/src/components/sdl/RentGpusForm.tsx (1)

49-49: LGTM! Config now sourced from DI container.

The change correctly retrieves publicConfig from the DI container and aliases it to appConfig for use within the component. This aligns with the PR objective to centralize service creation in the DI container and improves testability.

apps/deploy-web/src/components/settings/ExportCertificate.tsx (1)

6-6: LGTM! Clean DI container integration.

The change correctly sources analyticsService from the DI container via useServices(), following React hooks rules and improving testability as intended by the PR.

Also applies to: 11-11

apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.tsx (2)

47-64: LGTM - Correct refactoring to use DI container

The removal of UrlService from DEPENDENCIES and obtaining it via useServices() hook aligns with the PR objective to move service creation to the DI container. This improves testability by allowing services to be mocked via the dependencies prop.


75-84: LGTM - Services correctly obtained from DI container

The destructuring of services from useServices() is correctly implemented. The renaming of publicConfig to appConfig improves local readability while maintaining clarity.

apps/deploy-web/src/services/app-di-container/browser-di-container.ts (1)

39-39: LGTM - Config source correctly migrated to publicConfig.

The change from appConfig to publicConfig aligns with the DI container refactoring that exposes configuration via publicConfig. The access remains properly scoped within the factory function.

apps/deploy-web/src/components/templates/UserTemplate.tsx (1)

15-15: LGTM - Correctly migrated to context-based service access.

The component now properly retrieves analyticsService from the DI container via useServices(), eliminating the direct module import. This improves testability and follows the project's DI pattern.

Also applies to: 34-34

apps/deploy-web/src/components/deployments/DeploymentDepositModal.tsx (1)

12-12: LGTM - Service access properly migrated to useServices().

The modal correctly retrieves analyticsService from the DI container via useServices(), consistent with the refactoring pattern across the codebase.

Also applies to: 51-51

apps/deploy-web/src/components/new-deployment/TemplateList.tsx (1)

11-11: LGTM - Consistent with DI container refactoring.

The component correctly obtains analyticsService via useServices(), maintaining consistency with the broader refactoring effort.

Also applies to: 47-47

apps/deploy-web/src/pages/user/api-keys/index.tsx (1)

9-9: LGTM - Service access correctly refactored.

The page correctly retrieves analyticsService from the context via useServices(), following the established DI pattern.

Also applies to: 13-13

apps/deploy-web/src/services/app-di-container/app-di-container.ts (3)

36-36: LGTM - publicConfig properly exposed in the DI container.

The publicConfig getter correctly exposes browserEnvConfig through the container, enabling other services to access configuration in a testable manner.


71-76: LGTM - StripeService correctly configured with publicConfig.

The service is properly instantiated with the publishable key sourced from publicConfig, improving testability and aligning with the DI refactoring goals.


137-148: LGTM - AnalyticsService correctly instantiated with configuration from publicConfig.

The service is properly created within a factory function with amplitude and GA settings sourced from publicConfig. This eliminates the singleton pattern and improves testability.

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

75-91: LGTM - Test correctly updated for new constructor signature.

The setup function properly reflects the new StripeService constructor that accepts a config object with publishableKey. The test pattern follows the project guidelines with the setup function at the bottom and using jest-mock-extended.

apps/deploy-web/src/services/analytics/analytics.service.ts (1)

1-1: LGTM - Service correctly refactored for DI container usage.

The service now:

  • Uses proper value imports (not type-only) for amplitude and murmurhash
  • Accepts dependencies via constructor with appropriate defaults
  • Removes the singleton pattern, enabling proper DI
  • Includes "use client" directive for client-side execution

These changes align with the PR objective to improve testability and reduce side effects.

Also applies to: 3-5, 157-161

apps/deploy-web/src/services/stripe/stripe.service.ts (3)

3-8: LGTM! Clean dependency injection interface.

The interface properly separates configuration (config) from optional implementation dependencies (loadStripe), enabling both testability and production use.


12-12: LGTM! Correct use of Required utility type.

Using Required<StripeServiceDependencies> accurately reflects that the constructor ensures all dependencies are present after applying defaults.


14-19: LGTM! Constructor follows DI best practices.

The constructor correctly accepts dependencies as a parameter and applies defaults for optional dependencies. The spread order allows test overrides while providing production defaults.

@stalniy stalniy merged commit b771771 into main Dec 29, 2025
64 of 65 checks passed
@stalniy stalniy deleted the refactor/analytics-service branch December 29, 2025 12:21
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