Skip to content

feat(api-service): context bound preferences (admin facing API) fixes NV-6974#9821

Merged
ChmaraX merged 3 commits intonextfrom
nv-6974-preference-based-on-context-1
Jan 12, 2026
Merged

feat(api-service): context bound preferences (admin facing API) fixes NV-6974#9821
ChmaraX merged 3 commits intonextfrom
nv-6974-preference-based-on-context-1

Conversation

@ChmaraX
Copy link
Contributor

@ChmaraX ChmaraX commented Jan 9, 2026

What changed? Why was the change needed?

This pull request introduces support for context-aware subscriber preferences, allowing preferences to be set and filtered based on custom context keys (such as tenant or environment). It updates DTOs, use cases, and tests to handle context payloads, and adds feature flag controls for enabling or disabling this functionality. The changes also include improvements to test coverage for context-specific behavior and minor updates to code style and configuration.

Context-aware preferences support:

  • Added context field (with validation and max count) to DTOs for bulk update, patch, and get subscriber preferences, enabling context-specific preference operations. (apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts [1] [2]; apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts [3] [4]; apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts [5] [6]
  • Enhanced GetSubscriberPreferencesRequestDto to allow filtering by contextKeys via query parameters, with normalization and transformation logic for flexible usage. (apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts [1] [2]

Test coverage for context-aware behavior:

  • Extended end-to-end tests for subscriber preferences to cover context-specific preference creation, filtering, fallback, and isolation, as well as feature flag enablement. (apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts [1] [2] [3] [4] [5] [6]; apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts [7]

@linear
Copy link

linear bot commented Jan 9, 2026

@netlify
Copy link

netlify bot commented Jan 9, 2026

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit b171eb5
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/6964cde895ade300087bda47

@github-actions github-actions bot changed the title feat(api-service): context bound preferences (admin facing API) feat(api-service): context bound preferences (admin facing API) fixes NV-6974 Jan 9, 2026
@ChmaraX ChmaraX marked this pull request as ready for review January 12, 2026 10:12
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

Walkthrough

This pull request implements context-aware subscriber preferences functionality across the API and SDK. Changes include adding optional context fields to preference-related DTOs and commands, introducing ContextRepository and FeatureFlagsService dependencies for context handling, and updating database indexes to support contextKeys. The e2e test script is narrowed to subscriber-v2 tests, and new test cases validate context-based filtering and isolation. Multiple SDK functions are refactored to accept request objects instead of individual parameters, and spell-check ignore patterns are broadened. Database schema indexes are modified to include contextKeys for unique constraint enforcement.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding context-bound preferences support to the admin-facing API, with a specific ticket reference (NV-6974).
Description check ✅ Passed The PR description clearly outlines the context-aware preferences feature, specific changes to DTOs and use cases, and test coverage additions, all directly related to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @apps/api/package.json:
- Line 33: The npm script "test:e2e:novu-v2" narrows the test glob to
"src/**/subscribers-v2/e2e/**/*.e2e{,-ee}.ts" which excludes many #novu-v2
tests; update that script to use the broader glob "src/**/*.e2e{,-ee}.ts"
(keeping the existing flags like --grep '#novu-v2', NODE_OPTIONS, and other env
vars) so all v2-tagged end-to-end tests (workflows-v2, topics-v2, layouts-v2,
contexts, environments-v2, etc.) are included when running the script.

In @libs/internal-sdk/src/funcs/subscribersPreferencesList.ts:
- Around line 126-137: The fallback "|| { strategy: 'none' }" is unreachable
because the object literal before it is always truthy; remove that final
fallback or replace the chained ORs with nullish coalescing to preserve intended
semantics. Update the expression that sets retryConfig (the piece using
options?.retries || client._options.retryConfig || { strategy: 'backoff', ... }
|| { strategy: 'none' }) to either drop the trailing "|| { strategy: 'none' }"
or use ?? between operands (options?.retries ?? client._options.retryConfig ?? {
strategy: 'backoff', ... }) so the default backoff object is only used when
prior values are null/undefined; apply the same fix in other generated files
with the same pattern (notificationsList.ts, activityWorkflowRunsList.ts).
🧹 Nitpick comments (5)
apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts (1)

268-282: Consider verifying context persistence.

The test creates a preference with context but doesn't verify the context was actually stored. Consider adding a list call with contextKeys to confirm persistence, similar to the pattern used in the bulk update test (lines 356-360).

💡 Suggested verification
     expect(response.result.workflows).to.have.lengthOf(1);
     expect(response.result.workflows[0].channels).to.deep.equal({ inApp: true, email: false });
+
+    // Verify context was persisted
+    const listResponse = await novuClient.subscribers.preferences.list({
+      subscriberId: subscriber.subscriberId,
+      contextKeys: ['tenant:acme'],
+    });
+    expect(listResponse.result.workflows[0].channels.email).to.equal(false);
   });
.cspell.json (1)

854-855: Verify intent of broadened spell-check ignore patterns.

Replacing specific file ignores with **/*.e2e.ts and **/*.spec.ts excludes all e2e and spec files repository-wide from spell checking. While this reduces noise from test-specific identifiers and mock data, it may also hide legitimate spelling errors in user-facing test descriptions or error messages.

apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts (1)

17-17: Consider using a test utility for feature flag management.

Directly manipulating process.env with type assertion is fragile and could leak state between tests if an exception occurs before afterEach runs. Consider wrapping this in a try/finally pattern or using a dedicated test helper for feature flag mocking.

♻️ Suggested improvement
+  let originalEnvValue: string | undefined;
+
   beforeEach(async () => {
-    (process.env as any).IS_CONTEXT_PREFERENCES_ENABLED = 'true';
+    originalEnvValue = process.env.IS_CONTEXT_PREFERENCES_ENABLED;
+    process.env.IS_CONTEXT_PREFERENCES_ENABLED = 'true';
     const uuid = randomBytes(4).toString('hex');
     // ...
   });

   afterEach(() => {
-    delete (process.env as any).IS_CONTEXT_PREFERENCES_ENABLED;
+    if (originalEnvValue === undefined) {
+      delete process.env.IS_CONTEXT_PREFERENCES_ENABLED;
+    } else {
+      process.env.IS_CONTEXT_PREFERENCES_ENABLED = originalEnvValue;
+    }
   });

Also applies to: 28-30

apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts (2)

140-167: Consider extracting resolveContexts to a shared service.

This method appears to duplicate logic that exists in UpdateSubscriberPreferences (per the AI summary). Extracting this to a shared service would reduce duplication and ensure consistent behavior across use cases.

#!/bin/bash
# Verify if similar resolveContexts logic exists elsewhere
echo "=== Searching for similar resolveContexts implementations ==="
ast-grep --pattern $'async resolveContexts($$$) {
  $$$
}'

146-150: Consider aligning feature flag call signature with other usages.

The feature flag check here only passes organizationId, but other usages in the codebase (e.g., in idempotency.interceptor.ts) include environmentId and user._id for more granular control. Consider whether environment-level or user-level feature gating is needed.

♻️ Suggested improvement
     const isEnabled = await this.featureFlagsService.getFlag({
       key: FeatureFlagsKeysEnum.IS_CONTEXT_PREFERENCES_ENABLED,
       defaultValue: false,
+      environment: { _id: environmentId },
       organization: { _id: organizationId },
     });
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 115ea8e and d9e0bd0.

📒 Files selected for processing (28)
  • .cspell.json
  • apps/api/package.json
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • libs/dal/src/repositories/preferences/preferences.schema.ts
  • libs/internal-sdk/.speakeasy/gen.yaml
  • libs/internal-sdk/src/funcs/subscribersPreferencesList.ts
  • libs/internal-sdk/src/lib/config.ts
  • libs/internal-sdk/src/lib/sdks.ts
  • libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts
  • libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts
  • libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts
  • libs/internal-sdk/src/models/components/previewpayloaddto.ts
  • libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.ts
  • libs/internal-sdk/src/sdk/preferences.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use descriptive variable names with auxiliary verbs (isLoading, hasError)
Add blank lines before return statements
Import motion components from 'motion/react' instead of 'motion-react'

**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use functional and declarative programming patterns; avoid classes
Prefer iteration and modularization over code duplication, minimize code duplication as possible
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Structure files: exported component, subcomponents, helpers, static content, types
Don't leave comments in code, unless they explain something complex and not trivial
Don't use nested ternaries
Favor named exports for components
Use TypeScript for all code; prefer interfaces over types
In front end code, use types over interfaces
Use functional components with TypeScript types
Use the "function" keyword for pure functions
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
Add blank lines before return statements
When importing "motion-react" package, import it from "motion/react"

Files:

  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • libs/internal-sdk/src/lib/sdks.ts
  • libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • libs/internal-sdk/src/funcs/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
  • libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts
  • libs/internal-sdk/src/lib/config.ts
  • libs/internal-sdk/src/sdk/preferences.ts
  • libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts
  • libs/internal-sdk/src/models/components/previewpayloaddto.ts
  • libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts
  • libs/dal/src/repositories/preferences/preferences.schema.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use functional and declarative programming patterns; avoid classes

Files:

  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • libs/internal-sdk/src/lib/sdks.ts
  • libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • libs/internal-sdk/src/funcs/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
  • libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts
  • libs/internal-sdk/src/lib/config.ts
  • libs/internal-sdk/src/sdk/preferences.ts
  • libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts
  • libs/internal-sdk/src/models/components/previewpayloaddto.ts
  • libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts
  • libs/dal/src/repositories/preferences/preferences.schema.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Favor named exports for components

Files:

  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • libs/internal-sdk/src/lib/sdks.ts
  • libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • libs/internal-sdk/src/funcs/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
  • libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts
  • libs/internal-sdk/src/lib/config.ts
  • libs/internal-sdk/src/sdk/preferences.ts
  • libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts
  • libs/internal-sdk/src/models/components/previewpayloaddto.ts
  • libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts
  • libs/dal/src/repositories/preferences/preferences.schema.ts
apps/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer interfaces over types in backend code

Files:

  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
**

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

Use lowercase with dashes for directories and files (e.g., components/auth-wizard)

Files:

  • apps/api/src/app/subscribers-v2/subscribers.module.ts
  • apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts
  • apps/api/src/app/subscribers-v2/subscribers.controller.ts
  • apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts
  • libs/internal-sdk/src/lib/sdks.ts
  • libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts
  • apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts
  • apps/api/package.json
  • libs/internal-sdk/src/funcs/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts
  • apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts
  • apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts
  • apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.ts
  • apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts
  • libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts
  • libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts
  • libs/internal-sdk/src/lib/config.ts
  • libs/internal-sdk/src/sdk/preferences.ts
  • libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts
  • libs/internal-sdk/src/models/components/previewpayloaddto.ts
  • libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts
  • libs/dal/src/repositories/preferences/preferences.schema.ts
🧠 Learnings (8)
📚 Learning: 2026-01-07T13:09:45.904Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/novu.mdc:0-0
Timestamp: 2026-01-07T13:09:45.904Z
Learning: Applies to **/*.{ts,tsx} : Structure files: exported component, subcomponents, helpers, static content, types

Applied to files:

  • .cspell.json
📚 Learning: 2026-01-07T13:09:45.904Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/novu.mdc:0-0
Timestamp: 2026-01-07T13:09:45.904Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript for all code; prefer interfaces over types

Applied to files:

  • .cspell.json
📚 Learning: 2025-11-25T11:29:52.304Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T11:29:52.304Z
Learning: Applies to apps/dashboard/**/*.{ts,tsx} : Use lowercase with dashes for directories and files in dashboard (e.g., components/auth-wizard)

Applied to files:

  • .cspell.json
📚 Learning: 2026-01-07T13:09:45.904Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/novu.mdc:0-0
Timestamp: 2026-01-07T13:09:45.904Z
Learning: Applies to **/*.{ts,tsx} : Prefer iteration and modularization over code duplication, minimize code duplication as possible

Applied to files:

  • .cspell.json
📚 Learning: 2025-12-22T14:14:49.363Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-12-22T14:14:49.363Z
Learning: Applies to apps/dashboard/**/components/**/*.{ts,tsx} : Favor named exports for components

Applied to files:

  • .cspell.json
📚 Learning: 2025-11-25T11:29:52.304Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T11:29:52.304Z
Learning: Applies to apps/dashboard/**/*.{tsx,ts} : Favor named exports for components in dashboard

Applied to files:

  • .cspell.json
📚 Learning: 2025-11-25T11:29:52.304Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T11:29:52.304Z
Learning: Applies to apps/{dashboard,web}/**/*.{ts,tsx} : Prefer types over interfaces in frontend code

Applied to files:

  • .cspell.json
📚 Learning: 2026-01-07T13:09:45.904Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/novu.mdc:0-0
Timestamp: 2026-01-07T13:09:45.904Z
Learning: Applies to **/*.{ts,tsx} : Favor named exports for components

Applied to files:

  • .cspell.json
🧬 Code graph analysis (12)
apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts (2)
libs/dal/src/repositories/context/context.repository.ts (1)
  • ContextRepository (8-90)
apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts (1)
  • UpdateSubscriberPreferencesCommand (9-25)
apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts (2)
apps/api/src/app/shared/framework/swagger/context-payload.decorator.ts (1)
  • ApiContextPayload (32-39)
libs/application-generic/src/decorators/is-valid-context-payload.decorator.ts (1)
  • IsValidContextPayload (157-161)
libs/internal-sdk/src/lib/sdks.ts (4)
libs/internal-sdk/src/lib/security.ts (1)
  • SecurityState (43-49)
libs/internal-sdk/src/hooks/hooks.ts (1)
  • SDKHooks (23-132)
libs/internal-sdk/src/lib/encodings.ts (1)
  • encodeForm (180-180)
libs/internal-sdk/src/lib/config.ts (1)
  • SDK_METADATA (60-66)
libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts (2)
libs/internal-sdk/src/models/components/scheduledto.ts (1)
  • ScheduleDto$Outbound (476-479)
apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts (1)
  • PatchSubscriberPreferencesDto (26-50)
apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts (1)
libs/application-generic/src/decorators/is-valid-context-payload.decorator.ts (1)
  • IsValidContextPayload (157-161)
apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts (2)
apps/api/src/app/shared/framework/swagger/context-payload.decorator.ts (1)
  • ApiContextPayload (32-39)
libs/application-generic/src/decorators/is-valid-context-payload.decorator.ts (1)
  • IsValidContextPayload (157-161)
apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts (2)
libs/dal/src/repositories/context/context.repository.ts (1)
  • ContextRepository (8-90)
apps/api/src/app/shared/framework/idempotency.interceptor.ts (1)
  • isEnabled (51-75)
libs/internal-sdk/src/react-query/subscribersPreferencesList.ts (3)
libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts (1)
  • SubscribersControllerGetSubscriberPreferencesRequest (20-31)
libs/internal-sdk/src/react-query/_types.ts (2)
  • QueryHookOptions (95-100)
  • SuspenseQueryHookOptions (102-107)
libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts (2)
  • SubscribersPreferencesListQueryData (12-12)
  • buildSubscribersPreferencesListQuery (25-50)
libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts (1)
libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferenceitemdto.ts (2)
  • BulkUpdateSubscriberPreferenceItemDto (12-21)
  • BulkUpdateSubscriberPreferenceItemDto$Outbound (24-27)
libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts (5)
libs/internal-sdk/src/react-query/subscribersPreferencesList.ts (4)
  • SubscribersPreferencesListQueryData (26-26)
  • prefetchSubscribersPreferencesList (24-24)
  • buildSubscribersPreferencesListQuery (23-23)
  • queryKeySubscribersPreferencesList (25-25)
libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts (2)
  • SubscribersControllerGetSubscriberPreferencesResponse (33-36)
  • SubscribersControllerGetSubscriberPreferencesRequest (20-31)
libs/internal-sdk/src/core.ts (1)
  • NovuCore (13-13)
libs/internal-sdk/src/lib/sdks.ts (1)
  • RequestOptions (30-56)
libs/internal-sdk/src/funcs/subscribersPreferencesList.ts (1)
  • subscribersPreferencesList (35-55)
libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts (1)
libs/internal-sdk/src/models/errors/sdkvalidationerror.ts (1)
  • SDKValidationError (7-50)
libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts (2)
libs/internal-sdk/src/models/components/workspacedto.ts (1)
  • WorkspaceDto$outboundSchema (31-38)
libs/internal-sdk/src/models/components/authdto.ts (1)
  • AuthDto$outboundSchema (25-31)
⏰ 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). (15)
  • GitHub Check: Unit test backend services (@novu/ws)
  • GitHub Check: Unit test backend services (@novu/api-service)
  • GitHub Check: E2E test API / Test E2E
  • GitHub Check: Unit test backend services (@novu/worker)
  • GitHub Check: Unit test backend services (@novu/webhook)
  • GitHub Check: Validate OpenAPI
  • GitHub Check: Unit test backend services (@novu/inbound-mail)
  • GitHub Check: Unit test @novu internal packages
  • GitHub Check: Unit test @novu public NPM packages (except providers)
  • GitHub Check: Redirect rules - dashboard-v2-novu-staging
  • GitHub Check: Header rules - dashboard-v2-novu-staging
  • GitHub Check: Pages changed - dashboard-v2-novu-staging
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: publish_preview_packages
🔇 Additional comments (57)
libs/internal-sdk/src/models/components/createchannelconnectionrequestdto.ts (5)

1-7: LGTM! Generated code with correct imports.

This file is auto-generated by Speakeasy. The imports correctly reference zod/v3 and the related DTO modules for AuthDto and WorkspaceDto.


12-38: Type definitions are well-structured.

The CreateChannelConnectionRequestDtoContext2 provides a rich context object with required id and optional data, while the union type allows flexibility for simple string identifiers. The main DTO properly incorporates the context map and required fields.


47-62: Outbound schema and serialization follow consistent patterns.

The schema correctly uses z.record(z.any()) for the flexible data field, and the JSON serialization function properly validates through the outbound schema before stringifying.


70-82: Union schema uses z.lazy() appropriately.

The use of z.lazy() for the CreateChannelConnectionRequestDtoContext2$outboundSchema reference is a good practice to handle schema evaluation order and potential circular dependencies.


99-118: Main DTO outbound schema correctly composes nested schemas.

The schema properly references WorkspaceDto$outboundSchema and AuthDto$outboundSchema for nested object validation, and the context record uses the appropriate union schema for flexibility.

libs/internal-sdk/src/models/components/previewpayloaddto.ts (4)

1-14: Auto-generated file — no manual edits recommended.

This file is generated by Speakeasy and marked "DO NOT EDIT". Any manual changes will be overwritten on the next SDK regeneration. If there are issues with the generated output, they should be addressed in the API spec or Speakeasy configuration.

The imports and file structure look correct for the intended purpose.


19-43: Type definitions are consistent and correctly structured.

The renaming from generic names (Two, Context) to more descriptive names (PreviewPayloadDtoContext2, PreviewPayloadDtoContext) improves code readability. The union type allowing both rich context objects and simple strings provides flexibility for the API consumers.


46-128: Schema definitions are correctly implemented.

The Zod schemas properly mirror the type definitions with appropriate inbound/outbound variants. The use of z.lazy() for the union schemas (while not strictly necessary for non-recursive types) ensures proper schema evaluation order and is a safe pattern in generated code.


130-139: JSON helper functions follow consistent patterns.

The serialization helpers correctly use the outbound schemas for stringification and inbound schemas for parsing, with appropriate error messages for debugging failed parses.

libs/internal-sdk/src/models/operations/subscriberscontrollergetsubscriberpreferences.ts (5)

1-3: Auto-generated file - changes appear consistent with PR objectives.

This file is auto-generated by Speakeasy and marked as "DO NOT EDIT". The changes add contextKeys support for context-aware preferences filtering. Any modifications to this file should be made through the Speakeasy configuration/spec rather than direct edits.


20-31: LGTM - contextKeys field properly added to request type.

The new contextKeys field is correctly typed as Array<string> | undefined with helpful JSDoc documentation explaining the filtering use case (e.g., ["tenant:acme"]).


38-47: LGTM - Outbound schema and type correctly updated.

The z.nativeEnum(Criticality) is the appropriate Zod schema for TypeScript const enums. The outbound type correctly includes contextKeys and uses the hyphenated 'idempotency-key' format for HTTP header compatibility.


50-65: LGTM - Outbound schema correctly implements contextKeys validation.

The schema correctly:

  • Validates contextKeys as an optional array of strings
  • Defaults criticality to 'nonCritical'
  • Remaps idempotencyKey to the HTTP header format 'idempotency-key'

78-101: LGTM - Inbound schema and parsing function correctly implemented.

The inbound schema correctly transforms PascalCase response fields (Headers, Result) to camelCase (headers, result). The subscribersControllerGetSubscriberPreferencesResponseFromJSON function properly returns a SafeParseResult with SDKValidationError for type-safe error handling.

libs/internal-sdk/src/models/components/bulkupdatesubscriberpreferencesdto.ts (1)

1-87: Generated code — changes look correct.

This file is auto-generated by Speakeasy. The additions correctly introduce the Context2 rich context type and integrate it into BulkUpdateSubscriberPreferencesDto with:

  • Proper outbound schemas for serialization
  • Union type support (Context2 | string) for flexibility
  • Optional context field maintaining backward compatibility

Any modifications should be made in the OpenAPI spec or Speakeasy configuration rather than directly in this file.

libs/internal-sdk/src/models/components/patchsubscriberpreferencesdto.ts (1)

1-95: Auto-generated code - no changes recommended.

This file is auto-generated by Speakeasy and marked "DO NOT EDIT". The generated types and schemas for the new context-aware preferences functionality appear structurally correct:

  • The Two and Context types correctly model the rich context object with optional data payload
  • The outbound schemas properly validate the union type (Two | string) for context values
  • The JSON serialization functions delegate to the schemas appropriately

The generic type name Two is an artifact of OpenAPI union type generation (representing the second variant in a union), which could be addressed in the OpenAPI spec if more descriptive naming is desired.

libs/internal-sdk/.speakeasy/gen.yaml (2)

25-26: LGTM!

The persistentEdits configuration change to enabled: never is appropriate for SDK generation, ensuring clean regeneration without preserving manual edits.


95-95: LGTM!

Setting preserveModelFieldNames: false allows the SDK generator to apply consistent naming conventions (e.g., camelCase transformation) to model fields, which aligns with TypeScript conventions.

apps/api/src/app/subscribers-v2/e2e/patch-subscriber-preferences.e2e.ts (4)

27-40: LGTM!

Proper feature flag setup and teardown for test isolation. Using beforeEach/afterEach ensures the flag state doesn't leak between tests.


284-317: LGTM!

Excellent test coverage for context isolation. Properly verifies that preferences with different contexts coexist independently and can be retrieved using their respective context keys.


319-334: LGTM!

Good negative test case validating the business rule that context cannot be used with global preferences. The error message assertion ensures clear API feedback.


336-361: LGTM!

Complete test with both immediate response verification and persistence verification via list. Good pattern demonstrating the context-aware bulk update flow end-to-end.

libs/internal-sdk/src/lib/config.ts (1)

62-65: LGTM!

Auto-generated SDK metadata version updates reflecting the OpenAPI spec changes from this PR. The version bumps (3.11.03.12.0 for OpenAPI, 2.791.12.792.3 for generator) are consistent with adding new context-related fields.

.cspell.json (1)

890-891: LGTM!

Standard ignore entries for generated worker configuration type definition files.

libs/dal/src/repositories/preferences/preferences.schema.ts (3)

148-166: LGTM with same migration considerations.

Consistent addition of contextKeys to the subscription-based index. The same migration and query consistency considerations from the SUBSCRIBER_WORKFLOW index apply here.


110-112: LGTM!

Comments clearly document the intent: enabling multiple preferences per context while maintaining per-type uniqueness constraints.


109-128: Index migration and contextKeys handling are properly implemented.

The codebase already addresses the concerns raised:

  1. Migration Strategy – A dedicated preferences-uniqueness-migration.ts runs before the new index takes effect, deduplicating existing preferences by grouping on {_environmentId, _subscriberId, _templateId, type} and keeping the oldest record.

  2. Undefined vs. Empty Array vs. Populated Array – The buildContextExactMatchQuery() method (used consistently in both upsert and get operations) handles all three cases:

    • undefined or [] → matches {$or: [{contextKeys: {$exists: false}}, {contextKeys: []}]}
    • ['tenant:acme'] → matches {contextKeys: {$all: [...], $size: ...}}
  3. Query Consistency – Both upsert and get flows use identical query patterns, ensuring no mismatches during updates or lookups.

  4. Feature Flag ControlresolveContexts() explicitly controls contextKeys population based on the feature flag (IS_CONTEXT_PREFERENCES_ENABLED), returning undefined when disabled (backward compatible) and [] or an array of context keys when enabled.

  5. Uniqueness Constraint – The partial filter expression on the index ensures it only applies to SUBSCRIBER_WORKFLOW type, preventing conflicts with other preference types.

apps/api/src/app/subscribers-v2/e2e/get-subscriber-preferences.e2e.ts (3)

94-134: Good test coverage for contextKeys filtering.

This test properly validates that:

  1. All workflows are returned regardless of context filter
  2. Context-specific preferences are applied correctly (workflow1 with tenant:acme → email:false)
  3. Workflows without matching context fall back to defaults (workflow2 → email:true)

136-157: Good coverage for default fallback behavior.

This test verifies that when querying with a context that has no specific preference, the default/inherited settings are returned correctly.


159-192: Good isolation test for per-context preferences.

This test effectively validates that:

  1. Context-specific overrides work independently
  2. Global preferences remain unchanged
  3. Different contexts see their respective values
apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.usecase.ts (1)

35-36: LGTM: Context resolution integrated correctly.

The contextKeys are resolved early in the execution flow and properly passed to UpdatePreferencesCommand, ensuring context-aware preference updates.

Also applies to: 110-110

apps/api/src/app/subscribers-v2/subscribers.module.ts (1)

18-18: LGTM: ContextRepository properly registered.

The ContextRepository is correctly imported and added to DAL_MODELS, making it available for dependency injection in the subscribers module.

Also applies to: 75-75

apps/api/src/app/subscribers-v2/usecases/get-subscriber-preferences/get-subscriber-preferences.usecase.ts (1)

52-62: LGTM: contextKeys properly forwarded.

The contextKeys from the incoming command are correctly forwarded to GetSubscriberPreferenceCommand, enabling context-aware preference filtering in the downstream use case.

apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.spec.ts (1)

88-108: LGTM on mock setup, but consider adding context-related test coverage.

The new contextRepositoryMock and featureFlagsServiceMock are correctly initialized and wired into the BulkUpdatePreferences constructor. However, no tests exercise the context-aware flows introduced by this PR.

Consider adding test cases that verify:

  • Context validation when context is provided in the command
  • Feature flag behavior affecting context resolution
libs/internal-sdk/src/lib/sdks.ts (1)

1-3: Auto-generated file – skip manual review.

This file is generated by Speakeasy and marked "DO NOT EDIT." The changes appear to be quote normalization from double to single quotes, likely from regenerating the SDK. No manual review is necessary for auto-generated code.

apps/api/src/app/subscribers-v2/dtos/bulk-update-subscriber-preferences.dto.ts (1)

36-39: LGTM! Context field properly added with appropriate validation.

The optional context field is correctly decorated with:

  • @ApiContextPayload() for Swagger documentation
  • @IsOptional() for optional validation
  • @IsValidContextPayload({ maxCount: 5 }) for context-specific validation

This aligns with the validation pattern used in related DTOs and commands.

apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.command.ts (1)

21-24: LGTM! Context field correctly added to command.

The optional context field follows the established validation pattern with @IsValidContextPayload({ maxCount: 5 }), consistent with other commands in this PR.

apps/api/src/app/subscribers-v2/dtos/get-subscriber-preferences-request.dto.ts (2)

16-25: Good defensive transform logic for query parameter normalization.

The transform handles multiple edge cases well:

  • undefinedundefined (no filter)
  • ''[] (filter for default/no context)
  • Single value → array coercion
  • Filters out empty strings from arrays

This is a robust approach for handling query string variations.


15-34: No changes needed—code is correct.

The @IsOptional() decorator correctly skips validation when contextKeys is undefined. This is the documented behavior of class-validator, and the pattern of combining @IsOptional() with @IsArray() is used throughout the codebase (e.g., notification.step.ts, notification-step-variant.command.ts). Requests without contextKeys will not fail validation.

Likely an incorrect or invalid review comment.

apps/api/src/app/subscribers-v2/subscribers.controller.ts (3)

261-265: LGTM!

Context keys are correctly passed through from the query DTO to the command. The validation is handled by the DTO layer, and the controller appropriately forwards the data.


320-328: LGTM!

Context is properly extracted from the request body and passed to the BulkUpdatePreferencesCommand. This aligns with the pattern used in the update endpoint.


348-358: LGTM!

Context is correctly propagated from the request body to UpdateSubscriberPreferencesCommand, maintaining consistency with the bulk update endpoint.

apps/api/src/app/subscribers-v2/dtos/patch-subscriber-preferences.dto.ts (1)

45-49: LGTM!

The context field is properly defined with appropriate validation decorators. The @IsValidContextPayload({ maxCount: 5 }) constraint is consistent with the command layer validation.

apps/api/src/app/inbox/usecases/bulk-update-preferences/bulk-update-preferences.command.ts (1)

16-19: LGTM!

The context field is properly added with consistent validation (maxCount: 5) matching the DTO layer. The readonly modifier is appropriate for command objects.

libs/internal-sdk/src/sdk/preferences.ts (2)

1-3: Generated code - verify regeneration consistency.

This file is marked as generated by Speakeasy. Ensure the OpenAPI spec changes that drive this generation are committed and that the SDK is regenerated consistently.


21-26: Breaking change in list() method signature.

The list() method now accepts a single request object instead of individual parameters (subscriberId, criticality, idempotencyKey). This is a breaking change for SDK consumers.

Ensure this breaking change is documented and SDK versioning is updated appropriately (e.g., major version bump or migration guide).

apps/api/src/app/subscribers-v2/usecases/update-subscriber-preferences/update-subscriber-preferences.usecase.ts (2)

67-73: LGTM!

The validation correctly prevents context from being used with global preferences, which aligns with the feature design. The error message is clear and actionable.


75-102: Clarify the semantic distinction between undefined (feature flag off) and [] (feature on, no context).

The methods buildContextExactMatchQuery in both UpdatePreferences and GetSubscriberPreference treat undefined and [] identically (matching only "no context" preferences). While the feature flag is rechecked in these methods, creating correct behavior, the code conflates the semantic intent at lines 338-341 and line 339 respectively.

The current flow works correctly because the feature flag is checked twice, but the logic should be clarified. Consider adding explicit documentation or restructuring to make it clear that the semantic distinction between "FF off (ignore context)" and "FF on but no context provided" is intentionally unified at the query level.

libs/internal-sdk/src/react-query/subscribersPreferencesList.ts (3)

1-3: Generated code - verify consistency.

This file is Speakeasy-generated. The changes align with the SDK layer updates for context support.


36-45: LGTM!

The hook signature correctly accepts the request object pattern, maintaining consistency with the SDK's Preferences.list() method.


82-99: LGTM!

Query key structure correctly includes contextKeys in the parameters, ensuring proper cache isolation and invalidation for context-specific preferences.

libs/internal-sdk/src/react-query/subscribersPreferencesList.core.ts (3)

1-3: Generated code - verify consistency.

This core file is Speakeasy-generated and provides the foundational query building for subscriber preferences.


33-38: LGTM!

The query key construction correctly includes contextKeys from the request, ensuring React Query properly caches and invalidates preferences based on context filters.


52-60: LGTM!

The queryKeySubscribersPreferencesList function signature correctly includes contextKeys in the parameters type, maintaining type safety for cache key construction.

libs/internal-sdk/src/funcs/subscribersPreferencesList.ts (3)

1-3: Generated code: manual edits are discouraged.

This file is auto-generated by Speakeasy. Any issues identified below should ideally be addressed through generator configuration or templates rather than direct modification, as changes will be overwritten on regeneration.


99-102: New query parameters for context-aware preferences.

The addition of contextKeys and criticality query parameters aligns with the PR objective for context-bound preferences functionality.


160-181: Comprehensive error code handling.

The error codes array covers a wide range of HTTP status codes including client errors (4XX) and server errors (5XX). The retry configuration properly excludes non-retryable errors.

Comment on lines +126 to +137
retryConfig: options?.retries ||
client._options.retryConfig || {
strategy: 'backoff',
backoff: {
initialInterval: 1000,
maxInterval: 30000,
exponent: 1.5,
maxElapsedTime: 3600000,
},
retryConnectionErrors: true,
}
|| { strategy: "none" },
retryCodes: options?.retryCodes || ["408", "409", "429", "5XX"],
} || { strategy: 'none' },
retryCodes: options?.retryCodes || ['408', '409', '429', '5XX'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the actual file content around the specified lines
fd "subscribersPreferencesList.ts" libs/internal-sdk/src/funcs/ --type f

Repository: novuhq/novu

Length of output: 112


🏁 Script executed:

# Once we locate the file, read the exact content
cat -n libs/internal-sdk/src/funcs/subscribersPreferencesList.ts | sed -n '120,140p'

Repository: novuhq/novu

Length of output: 826


🏁 Script executed:

# Check if similar dead code pattern exists in other generated SDK files
rg -n "strategy: 'none'" libs/internal-sdk/src/funcs/

Repository: novuhq/novu

Length of output: 328


Remove unreachable fallback: || { strategy: 'none' } is dead code.

The object literal spanning lines 127–135 is always truthy, making the fallback at line 136 unreachable. This pattern appears in multiple generated SDK files (notificationsList.ts, activityWorkflowRunsList.ts), suggesting a generator template issue to address in Speakeasy.

Diff
     retryConfig: options?.retries ||
       client._options.retryConfig || {
         strategy: 'backoff',
         backoff: {
           initialInterval: 1000,
           maxInterval: 30000,
           exponent: 1.5,
           maxElapsedTime: 3600000,
         },
         retryConnectionErrors: true,
-      } || { strategy: 'none' },
+      },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
retryConfig: options?.retries ||
client._options.retryConfig || {
strategy: 'backoff',
backoff: {
initialInterval: 1000,
maxInterval: 30000,
exponent: 1.5,
maxElapsedTime: 3600000,
},
retryConnectionErrors: true,
}
|| { strategy: "none" },
retryCodes: options?.retryCodes || ["408", "409", "429", "5XX"],
} || { strategy: 'none' },
retryCodes: options?.retryCodes || ['408', '409', '429', '5XX'],
retryConfig: options?.retries ||
client._options.retryConfig || {
strategy: 'backoff',
backoff: {
initialInterval: 1000,
maxInterval: 30000,
exponent: 1.5,
maxElapsedTime: 3600000,
},
retryConnectionErrors: true,
},
retryCodes: options?.retryCodes || ['408', '409', '429', '5XX'],
🤖 Prompt for AI Agents
In @libs/internal-sdk/src/funcs/subscribersPreferencesList.ts around lines 126 -
137, The fallback "|| { strategy: 'none' }" is unreachable because the object
literal before it is always truthy; remove that final fallback or replace the
chained ORs with nullish coalescing to preserve intended semantics. Update the
expression that sets retryConfig (the piece using options?.retries ||
client._options.retryConfig || { strategy: 'backoff', ... } || { strategy:
'none' }) to either drop the trailing "|| { strategy: 'none' }" or use ??
between operands (options?.retries ?? client._options.retryConfig ?? { strategy:
'backoff', ... }) so the default backoff object is only used when prior values
are null/undefined; apply the same fix in other generated files with the same
pattern (notificationsList.ts, activityWorkflowRunsList.ts).

@ChmaraX ChmaraX merged commit 3ee8fef into next Jan 12, 2026
35 checks passed
@ChmaraX ChmaraX deleted the nv-6974-preference-based-on-context-1 branch January 12, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant