Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/models/notification/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Notification models barrel export.
* Notification & Subscription models barrel export.
*/

export * from './notifications.types';
export * from './notifications.models';
export * from './subscriptions.types';
export * from './subscriptions.models';
17 changes: 17 additions & 0 deletions src/models/notification/notifications.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ export enum NotificationCategory {
Fatal = 'Fatal',
}

/**
* Notification delivery channel.
*
* `InApp` is always implicitly enabled (it is not returned by `getSupportedChannels()`);
* the others must be checked via `Subscriptions.getSupportedChannels()`.
*/
export enum NotificationMode {
/** Real-time in-app push. Always available. */
InApp = 'InApp',
/** Email delivery. */
Email = 'Email',
/** Slack delivery. */
Slack = 'Slack',
/** Microsoft Teams delivery. */
Teams = 'Teams',
}

/**
* Notification entry as returned by `GET /odata/v1/NotificationEntry`.
*
Expand Down
104 changes: 104 additions & 0 deletions src/models/notification/subscriptions.models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Subscription service model — public response shapes and the ServiceModel interface
* that drives generated API documentation.
*/

import type {
SubscriptionGetAllOptions,
SubscriptionGetPublishersOptions,
SubscriptionGetPublishersResponse,
SubscriptionGetResponse,
SubscriptionGetSupportedChannelsResponse,
} from './subscriptions.types';

/**
* Public surface of the Subscriptions service. JSDoc on this interface drives
* the generated API reference documentation.
*
* Every method takes the tenant GUID as the first argument, which the SDK
* forwards to the subscription API on each call.
*/
export interface SubscriptionServiceModel {
/**
* Gets the current user's subscription preferences, optionally filtered to a set of
* publisher names.
*
* Returns the full list of publishers (their topics, channels, and current subscription
* state) the user has access to. Use {@link SubscriptionGetAllOptions.publishers} to
* narrow to specific publishers.
*
* @param tenantId - Tenant GUID
* @param options - Optional publisher-name filter
* @returns Full subscription state for the matched publishers
* {@link SubscriptionGetResponse}
*
* @example Basic usage
* ```typescript
* import { Subscriptions } from '@uipath/uipath-typescript/notifications';
*
* const subscriptions = new Subscriptions(sdk);
* const { publishers } = await subscriptions.getAll('<tenantId>');
* ```
*
* @example Filter to specific publishers
* ```typescript
* const { publishers } = await subscriptions.getAll('<tenantId>', {
* publishers: ['Orchestrator', 'Actions'],
* });
* ```
* @internal
*/
getAll(tenantId: string, options?: SubscriptionGetAllOptions): Promise<SubscriptionGetResponse>;

/**
* Lists available publishers and their topics, regardless of the user's current subscription.
*
* Used for discovery — pair with {@link getAll} to inspect what's subscribable.
*
* Note: the response from this endpoint carries only identity/discovery fields on
* publishers and topics (no subscription state). Use {@link getAll} to inspect state.
*
* @param tenantId - Tenant GUID
* @param options - Optional publisher-name filter
* @returns Publishers and their full topic catalogue (discovery fields only)
* {@link SubscriptionGetPublishersResponse}
*
* @example Basic usage
* ```typescript
* const { publishers } = await subscriptions.getPublishers('<tenantId>');
* ```
*
* @example Filter to a single publisher
* ```typescript
* const { publishers } = await subscriptions.getPublishers('<tenantId>', { name: 'Orchestrator' });
* ```
* @internal
*/
getPublishers(tenantId: string, options?: SubscriptionGetPublishersOptions): Promise<SubscriptionGetPublishersResponse>;

/**
* Gets the notification channels supported in the current tenant.
*
* Check the `isEnabled` field on each {@link SupportedChannel} before attempting to subscribe to a
* channel — disabled channels will be rejected by the server.
*
* Note: `InApp` is always available and is not included in the response.
*
* @param tenantId - Tenant GUID
* @returns Supported channels with enabled status
* {@link SubscriptionGetSupportedChannelsResponse}
*
* @example
* ```typescript
* import { NotificationMode } from '@uipath/uipath-typescript/notifications';
*
* const { channels } = await subscriptions.getSupportedChannels('<tenantId>');
* const slack = channels.find(c => c.name === NotificationMode.Slack);
* if (slack?.isEnabled) {
* // safe to subscribe to Slack
* }
* ```
* @internal
*/
getSupportedChannels(tenantId: string): Promise<SubscriptionGetSupportedChannelsResponse>;
}
241 changes: 241 additions & 0 deletions src/models/notification/subscriptions.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/**
* Subscription service types — request/response shapes for user subscription preferences.
*/

import { NotificationCategory, NotificationMode } from './notifications.types';

/**
* Status of a notification mode (channel) for a publisher — whether the user has
* activated this channel.
*/
export interface AllowedMode {
/** Notification channel. */
name: NotificationMode;
/** Whether the user has activated this channel for the publisher. */
isActive: boolean;
}

/**
* Per-mode subscription state for a topic.
*/
export interface SubscriptionMode {
/** Notification channel. */
name: NotificationMode;
/** Whether the user is subscribed to this topic via this channel. */
isSubscribed: boolean;
/** Whether the topic is subscribed by default via this channel. */
isSubscribedByDefault: boolean;
}

/**
* Base topic shape — identity/discovery fields only, with no subscription state.
*/
export interface SubscriptionTopicBase {
/** Topic GUID. */
id: string;
/** Stable topic identifier (e.g. `Process.JobExecution.Faulted`). */
name: string;
/** Human-readable topic name. */
displayName: string | null;
/** Topic description. */
description: string | null;
/** Topic group name. */
group: string | null;
}

/**
* A topic that the user can subscribe to, with full subscription state — as returned by `getAll()`.
*/
export interface SubscriptionTopic extends SubscriptionTopicBase {
/** Severity category. */
category: NotificationCategory;
/** Parent topic group name. Often `null`. */
parentGroup: string | null;
/** Whether the user is currently subscribed to this topic. */
isSubscribed: boolean;
/** Whether the topic is mandatory — cannot be unsubscribed. */
isMandatory: boolean;
/** Whether the topic should be visible in the user-facing subscription UI. */
isVisible: boolean;
/** Whether the topic is subscribed by default for new users. */
isDefault: boolean;
/** Whether notifications for this topic can be batched. */
isAllowedToBeDispatchedInBatch: boolean;
/** Whether the topic is marked as infrequent (lower-priority delivery). */
isInfrequent: boolean;
/** Number of days notifications for this topic are retained. */
retentionDays: number;
/** Display ordering hint. */
orderingSequence: number;
/** Per-channel subscription state. */
modes: SubscriptionMode[];
}

/**
* An entity reference used in publisher / topic-group subscription requests.
*/
export interface SubscriptionEntity {
/** Entity GUID. */
id: string;
/** Entity name. */
name: string | null;
/** Entity type. */
type: string | null;
/** Parent name (e.g. folder name for a sub-folder). */
parentName: string | null;
/** Whether the user is subscribed to notifications for this entity. */
isSubscribed: boolean;
}

/**
* Group of entities that belong to a topic group.
*/
export interface TopicGroupEntity {
/** Topic group name. */
name: string | null;
/** Entities belonging to this group. */
entities: SubscriptionEntity[] | null;
}

/**
* Entity-sync operation an event maps to.
*/
export enum EntitySyncOperation {
Add = 'Add',
Delete = 'Delete',
}

/**
* Event mapping that triggers entity synchronization for an entity type.
*/
export interface EntitySyncEvent {
/** Source event name. */
eventName: string | null;
/** Sync operation the event maps to. */
operation: EntitySyncOperation;
}

/**
* Entity-type metadata declared by a publisher (e.g. folder entity registration).
*/
export interface SubscriptionEntityType {
/** Entity type name. */
type: string | null;
/** URL template for resolving entity links. */
urlTemplate: string | null;
/** Property used to project the entity in publications. */
projectionProperty: string | null;
/** Publication payload property carrying the entity reference. */
publicationPayloadProperty: string | null;
/** Request type used for entity sync. */
requestType: string | null;
/** Payload template for entity sync requests. */
payload: string | null;
/** Events that trigger entity synchronization. */
entitySyncEvents: EntitySyncEvent[] | null;
}

/**
* Entity types supported by a topic group.
*/
export interface TopicGroupEntityType {
/** Topic group name. */
name: string | null;
/** Entity type names. */
entityTypes: string[] | null;
}

/**
* Base publisher shape — identity/discovery fields and the topic catalogue, with no subscription state.
*/
export interface SubscriptionPublisherBase {
/** Publisher GUID. */
id: string;
/** Stable publisher name (e.g. `Orchestrator`, `Actions`). */
name: string;
/** Human-readable publisher name. */
displayName: string | null;
/** Topics published under this publisher. */
topics: SubscriptionTopicBase[];
}

/**
* A publisher with its topics, channels, and subscription state — as returned by `getAll()`.
*/
export interface SubscriptionPublisher extends SubscriptionPublisherBase {
/** Topics published under this publisher, with full per-topic subscription state. */
topics: SubscriptionTopic[];
/** URL to navigate to when a publisher notification is clicked. */
redirectionUrl: string | null;
/** Number of days notifications from this publisher are retained. */
retentionDays: number;
/** Whether notifications from this publisher are included in summary digests. */
addToSummary: boolean;
/** Whether the user has opted in to receive notifications from this publisher. */
isUserOptin: boolean;
/** Channels available for this publisher and their activation state. */
modes: AllowedMode[];
/** Entities (e.g. folders) the user has entity-level subscriptions for. */
entities: SubscriptionEntity[] | null;
/** Entity types the publisher exposes. */
entityTypes: SubscriptionEntityType[] | null;
/** Topic-group entity sets the user has entity-level subscriptions for. */
topicGroupEntities: TopicGroupEntity[];
/** Topic-group entity types. */
topicGroupEntityTypes: TopicGroupEntityType[] | null;
}

/**
* Channel availability returned by `getSupportedChannels()`.
*
* Note: `InApp` is never returned — it is always implicitly available.
*/
export interface SupportedChannel {
/** Notification channel. */
name: NotificationMode;
/** Whether the channel is enabled for the current tenant. */
isEnabled: boolean;
}

/**
* Options for `Subscriptions.getAll()`.
*/
export interface SubscriptionGetAllOptions {
/** Filter to specific publisher names. When omitted, all publishers are returned. */
publishers?: string[];
}

/**
* Options for `Subscriptions.getPublishers()`.
*/
export interface SubscriptionGetPublishersOptions {
/** Filter to a specific publisher name. When omitted, all publishers are returned. */
name?: string;
}

/**
* Response from `getAll()` — publishers with their topics, channels, and full subscription state.
*/
export interface SubscriptionGetResponse {
/** Publishers with their topics and subscription state. */
publishers: SubscriptionPublisher[];
}
Comment on lines +219 to +222

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are these responses wrapped? Lets not wrap the responses

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.

We have the same pattern in our API contract. It's done to keep both DTOs aligned.
Think of a scenario where we want to add a field to this DTO in addition to the publishers. We can simply add it in both places without breaking compatibility.
It's better to keep them aligned.
Also, what benefit do you see in unwrapping them?


/**
* Response from `getPublishers()` — the publisher/topic discovery catalogue.
*
* Publishers and topics carry only identity/discovery fields (no subscription state);
* use `getAll()` to inspect subscription state.
*/
export interface SubscriptionGetPublishersResponse {
/** Publishers with their topic catalogue. */
publishers: SubscriptionPublisherBase[];
}
Comment on lines +230 to +233

@Sarath1018 Sarath1018 Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here and for below method as well

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.

same reply


/**
* Response from `getSupportedChannels()`.
*/
export interface SubscriptionGetSupportedChannelsResponse {
/** Notification channels supported in the current tenant. `InApp` is not listed — it is always available. */
channels: SupportedChannel[];
}
Loading
Loading