Skip to content

feat(subscriptions): topic-level updates (PR 5/7)#517

Open
sarthak688 wants to merge 7 commits into
feat/subscriptions-sdkfrom
feat/subscriptions-topic-updates
Open

feat(subscriptions): topic-level updates (PR 5/7)#517
sarthak688 wants to merge 7 commits into
feat/subscriptions-sdkfrom
feat/subscriptions-topic-updates

Conversation

@sarthak688

Copy link
Copy Markdown
Contributor

PR 5/7 in the Notification SDK stack. Stacked on top of #516.

Adds the topic-scoped subscription writes. Each method takes an array of update entries, letting callers batch many subscription toggles into one request.

Methods Added

Layer Method Signature
Service subscriptions.updateTopic() updateTopic(tenantId: string, subscriptions: TopicSubscriptionUpdate[]): Promise<SubscriptionUpdateTopicResponse>
Service subscriptions.updateCategory() updateCategory(tenantId: string, subscriptions: CategorySubscriptionUpdate[]): Promise<SubscriptionUpdateCategoryResponse>

Endpoints Called

Method HTTP Endpoint OAuth Scope
updateTopic() POST notificationservice_/usersubscriptionservice/api/v1/UserSubscription NotificationService
updateCategory() POST notificationservice_/usersubscriptionservice/api/v1/UserSubscription/CategorySubscription NotificationService
  • UPDATE_TOPIC reuses the same URL as GET_ALL — POST vs GET differentiates the operation. Documented inline in the endpoint constants.
  • updateTopic sends { userSubscriptions: [...] }; updateCategory sends { categorySubscriptions: [...] }.
  • Each entry is a (topicId|publisherId, isSubscribed, notificationMode) triple.

Example Usage

import { NotificationMode, NotificationCategory } from '@uipath/uipath-typescript/notifications';

// Unsubscribe a single topic from a single channel
await subscriptions.updateTopic(tenantId, [
  { topicId: '<topicId>', isSubscribed: false, notificationMode: NotificationMode.Email },
]);

// Unsubscribe from all Error topics under one publisher via email
await subscriptions.updateCategory(tenantId, [
  {
    publisherId: '<publisherId>',
    category: NotificationCategory.Error,
    isSubscribed: false,
    notificationMode: NotificationMode.Email,
  },
]);

API Response vs SDK Response

Method SDK Response
updateTopic { success: true, data: { subscriptions } } (echoes input)
updateCategory { success: true, data: { subscriptions } } (echoes input)

Verification

Check Status
npm run typecheck ✅ clean
npm run lint ✅ 0 warnings, 0 errors
npm run test:unit ✅ 26 tests in notification suite (4 new)

Integration: updateTopic round-trip — flip a non-mandatory topic's mode, then restore. updateCategory is unit-only because it needs richer tenant fixtures (multi-topic category coverage).

Files

Area Files
Endpoint constants src/utils/constants/endpoints/notification.ts (UPDATE_TOPIC, UPDATE_CATEGORY)
Types src/models/notification/subscriptions.types.ts (TopicSubscriptionUpdate, CategorySubscriptionUpdate)
Models src/models/notification/subscriptions.models.ts (response types + ServiceModel methods)
Service src/services/notification/subscriptions.ts
Unit tests tests/unit/services/notification/subscriptions.test.ts (+4 tests)
Integration tests tests/integration/shared/notification/subscriptions.integration.test.ts (+1 round-trip test)
Test utils tests/utils/constants/notification.ts (ERROR_SUBSCRIPTION_INVALID)
Docs docs/oauth-scopes.md

PR Stack

# Branch Status
1 feat/notifications-sdk #512
2 feat/notifications-mark-read #513
3 feat/notifications-delete #514
4 feat/subscriptions-sdk #516
5 feat/subscriptions-topic-updates (this PR)
6 feat/subscriptions-publisher-updates upcoming
7 feat/subscriptions-mode-reset upcoming

🤖 Generated with Claude Code

sarthak688 and others added 5 commits June 12, 2026 11:52
Establishes the notification service module with the inbox listing
endpoint. Routes at the organization level via NOTIFICATION_BASE
(URL-traversal trick to bypass the tenant segment). tenantId is taken
as a positional argument and forwarded via X-UIPATH-Internal-TenantId.

Foundation included for follow-up PRs that add the remaining inbox and
subscription methods on top of this branch:
  - @uipath/uipath-typescript/notifications subpath in package.json + rollup
  - NOTIFICATION_BASE, NOTIFICATION_ENDPOINTS scaffold
  - test infra (NOTIFICATION_TEST_TENANT_ID env var, test-config,
    unified-setup registration, notification mock factories, constants)
  - oauth-scopes + pagination + mkdocs nav entries

Methods: getAll (paginated OData listing with $top/$skip/$count,
filter, orderby; transformFn drops 8 internal/transport fields).

Tests: 4 unit + 4 integration (skipped when NOTIFICATION_TEST_TENANT_ID
is not set).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…llRead)

Adds the three read-state mutation methods on top of the foundation
established in feat/notifications-sdk. All three POST to the
NotificationEntry.UpdateRead OData action; markAllRead uses the
server-side forceAllRead flag.

Adds UPDATE_READ endpoint constant and per-method telemetry decorators.

Tests: 6 additional unit tests + 2 integration tests (mark/unmark
round-trip + markAllRead).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the two delete methods. Both POST to the NotificationEntry.DeleteBulk
OData action; deleteAll uses the server-side deleteAll flag.

Preserves the API spec's misspelling `notifcationIds` in the request body —
the server expects that exact (mistyped) key.

Tests: 4 additional unit tests. No integration tests — these destructively
mutate the inbox with no SDK-level undo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the SubscriptionService class alongside the existing
NotificationService. Both services live under
@uipath/uipath-typescript/notifications as discrete classes (no shared
state). Subscription endpoints route through the same notificationservice_
base via the URL-traversal trick.

Methods: getAll, getPublishers, getSupportedChannels — the three read-side
methods that return user subscription state and tenant channel availability.
Mutation methods land in follow-up PRs.

Adds SUBSCRIPTION_ENDPOINTS scaffold (3 read URLs), subscription model
types (SubscriptionPublisher, SubscriptionTopic, SubscriptionMode,
SupportedChannel, AllowedMode, options types), subscription test
infrastructure (mock factories), Subscriptions section in oauth-scopes
and mkdocs nav, and Subscriptions registration in unified-setup.

Tests: 8 unit + 4 integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gory)

Adds the two topic/category-scoped subscription writes. Each entry in
the payload sets the subscription state for a single (topic, mode) or
(category, mode) pair.

UPDATE_TOPIC reuses the same URL as GET_ALL — POST vs GET differentiates.

Adds TopicSubscriptionUpdate and CategorySubscriptionUpdate types,
response types, oauth-scopes entries, and unit + integration tests
(updateTopic round-trip; updateCategory unit-only because it needs
richer tenant fixtures).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines +210 to +211
it('should propagate errors', async () => {
mockApiClient.post.mockRejectedValue(createMockError(TEST_CONSTANTS.ERROR_MESSAGE));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The updateCategory error test uses the generic TEST_CONSTANTS.ERROR_MESSAGE while the parallel updateTopic error test (line 174) correctly uses NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID. Per conventions, TEST_CONSTANTS.ERROR_MESSAGE is only acceptable for collection methods like getAll — mutation methods should use domain-specific error constants.

Suggested change
it('should propagate errors', async () => {
mockApiClient.post.mockRejectedValue(createMockError(TEST_CONSTANTS.ERROR_MESSAGE));
it('should propagate errors', async () => {
mockApiClient.post.mockRejectedValue(createMockError(NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in a2057e0 — switched to NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID.

notificationMode: NotificationMode.InApp,
},
])
).rejects.toThrow(TEST_CONSTANTS.ERROR_MESSAGE);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
).rejects.toThrow(TEST_CONSTANTS.ERROR_MESSAGE);
).rejects.toThrow(NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in a2057e0 — switched to NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID.

@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review findings (1 issue):

New finding posted:

  • tests/unit/services/notification/subscriptions.test.ts lines 210–222 — updateCategory error test uses TEST_CONSTANTS.ERROR_MESSAGE (generic) instead of NOTIFICATION_TEST_CONSTANTS.ERROR_SUBSCRIPTION_INVALID (domain-specific), inconsistent with the parallel updateTopic error test. Per conventions, the generic constant is only acceptable for collection methods like getAll.

…ory test

Matches the parallel updateTopic error test on the same suite.

Addresses review comment on PR #517.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarthak688

Copy link
Copy Markdown
Contributor Author

All findings addressed in the latest commit on this branch. Inline reply threads are resolved with commit references.

…scopes entries

Same internal-scope treatment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sarthak688 sarthak688 force-pushed the feat/subscriptions-sdk branch 3 times, most recently from 1d382c9 to 18c7fcb Compare July 9, 2026 07:15
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.

1 participant