@foru-ms/sdk 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +375 -38
  2. package/dist/Client.d.ts +63 -4
  3. package/dist/Client.js +124 -22
  4. package/dist/errors.d.ts +52 -0
  5. package/dist/errors.js +95 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +3 -0
  8. package/dist/resources/Integrations.d.ts +7 -0
  9. package/dist/resources/Integrations.js +6 -0
  10. package/dist/resources/Posts.d.ts +12 -0
  11. package/dist/resources/Posts.js +44 -0
  12. package/dist/resources/PrivateMessages.d.ts +4 -0
  13. package/dist/resources/PrivateMessages.js +6 -0
  14. package/dist/resources/SSO.d.ts +10 -0
  15. package/dist/resources/SSO.js +11 -0
  16. package/dist/resources/Tags.d.ts +8 -0
  17. package/dist/resources/Tags.js +24 -0
  18. package/dist/resources/Threads.d.ts +20 -0
  19. package/dist/resources/Threads.js +85 -0
  20. package/dist/resources/Users.d.ts +10 -0
  21. package/dist/resources/Users.js +26 -0
  22. package/dist/resources/Webhooks.d.ts +69 -0
  23. package/dist/resources/Webhooks.js +115 -0
  24. package/dist/response-types.d.ts +105 -0
  25. package/dist/response-types.js +2 -0
  26. package/dist/utils.d.ts +80 -0
  27. package/dist/utils.js +138 -0
  28. package/examples/README.md +38 -0
  29. package/examples/authentication.ts +79 -0
  30. package/examples/error-handling.ts +133 -0
  31. package/examples/managing-threads.ts +130 -0
  32. package/examples/pagination.ts +81 -0
  33. package/examples/webhooks.ts +176 -0
  34. package/package.json +1 -1
  35. package/src/Client.ts +165 -25
  36. package/src/errors.ts +95 -0
  37. package/src/index.ts +3 -0
  38. package/src/resources/Integrations.ts +11 -0
  39. package/src/resources/Posts.ts +56 -0
  40. package/src/resources/PrivateMessages.ts +10 -0
  41. package/src/resources/SSO.ts +17 -0
  42. package/src/resources/Tags.ts +32 -0
  43. package/src/resources/Threads.ts +109 -0
  44. package/src/resources/Users.ts +36 -0
  45. package/src/resources/Webhooks.ts +131 -0
  46. package/src/response-types.ts +113 -0
  47. package/src/utils.ts +182 -0
@@ -0,0 +1,176 @@
1
+ import { ForumClient } from '@foru-ms/sdk';
2
+ import express from 'express';
3
+
4
+ /**
5
+ * Example: Webhooks
6
+ * Demonstrates setting up, handling, and verifying webhooks
7
+ */
8
+
9
+ async function setupWebhooks() {
10
+ const client = new ForumClient({
11
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
12
+ });
13
+
14
+ // Example 1: Create a Webhook
15
+ console.log('=== Creating a Webhook ===');
16
+ const webhook = await client.webhooks.create({
17
+ name: 'Thread Activity Monitor',
18
+ url: 'https://your-domain.com/webhooks/foru',
19
+ events: [
20
+ 'thread.created',
21
+ 'thread.updated',
22
+ 'thread.deleted',
23
+ 'post.created',
24
+ ],
25
+ });
26
+ console.log('Webhook created:', webhook.webhook.id);
27
+ console.log('Secret:', webhook.webhook.secret); // Save this securely!
28
+
29
+ // Example 2: List All Webhooks
30
+ console.log('\n=== Listing Webhooks ===');
31
+ const webhooks = await client.webhooks.list();
32
+ console.log('Total webhooks:', webhooks.webhooks.length);
33
+
34
+ // Example 3: Update a Webhook
35
+ console.log('\n=== Updating a Webhook ===');
36
+ await client.webhooks.update(webhook.webhook.id, {
37
+ active: true,
38
+ events: [
39
+ 'thread.created',
40
+ 'thread.updated',
41
+ 'thread.deleted',
42
+ 'post.created',
43
+ 'post.updated', // Added new event
44
+ ],
45
+ });
46
+ console.log('Webhook updated');
47
+
48
+ // Example 4: Get Webhook Deliveries
49
+ console.log('\n=== Getting Webhook Deliveries ===');
50
+ const deliveries = await client.webhooks.getDeliveries(webhook.webhook.id);
51
+ console.log('Total deliveries:', deliveries.total);
52
+ console.log('Recent deliveries:', deliveries.deliveries.slice(0, 5));
53
+
54
+ return webhook.webhook.secret;
55
+ }
56
+
57
+ /**
58
+ * Example webhook handler using Express
59
+ */
60
+ function setupWebhookHandler(secret: string) {
61
+ const app = express();
62
+ const client = new ForumClient({
63
+ apiKey: process.env.FORU_API_KEY || 'your_api_key',
64
+ });
65
+
66
+ // Important: Use raw body for signature verification
67
+ app.use(express.json({
68
+ verify: (req: any, res, buf) => {
69
+ req.rawBody = buf.toString();
70
+ }
71
+ }));
72
+
73
+ app.post('/webhooks/foru', (req: any, res) => {
74
+ // Example 5: Verify Webhook Signature with Timestamp
75
+ const signature = req.headers['x-webhook-signature'];
76
+ const timestamp = req.headers['x-webhook-timestamp'];
77
+ const event = req.headers['x-webhook-event'];
78
+
79
+ // Verify timestamp age (prevent replay attacks)
80
+ const age = Date.now() - parseInt(timestamp);
81
+ if (age > 5 * 60 * 1000) { // 5 minutes
82
+ console.error('Webhook timestamp too old');
83
+ return res.status(400).send('Webhook timestamp too old');
84
+ }
85
+
86
+ // Verify signature
87
+ const isValid = client.webhooks.verifySignature(
88
+ req.body,
89
+ signature,
90
+ timestamp,
91
+ secret
92
+ );
93
+
94
+ if (!isValid) {
95
+ console.error('Invalid webhook signature');
96
+ return res.status(401).send('Invalid signature');
97
+ }
98
+
99
+ // Process webhook payload
100
+ console.log('Received webhook event:', event);
101
+ const payload = req.body;
102
+
103
+ switch (event) {
104
+ case 'thread.created':
105
+ handleThreadCreated(payload);
106
+ break;
107
+ case 'thread.updated':
108
+ handleThreadUpdated(payload);
109
+ break;
110
+ case 'thread.deleted':
111
+ handleThreadDeleted(payload);
112
+ break;
113
+ case 'post.created':
114
+ handlePostCreated(payload);
115
+ break;
116
+ default:
117
+ console.log('Unhandled event type:', event);
118
+ }
119
+
120
+ // Always respond quickly to acknowledge receipt
121
+ res.status(200).send('OK');
122
+ });
123
+
124
+ const PORT = process.env.PORT || 3000;
125
+ app.listen(PORT, () => {
126
+ console.log(`\nWebhook handler listening on port ${PORT}`);
127
+ console.log(`Endpoint: http://localhost:${PORT}/webhooks/foru`);
128
+ });
129
+ }
130
+
131
+ // Event handler functions
132
+ function handleThreadCreated(thread: any) {
133
+ console.log('New thread created:', thread.id);
134
+ console.log('Title:', thread.title);
135
+ console.log('Author:', thread.userId);
136
+
137
+ // Example: Send notification, update database, etc.
138
+ }
139
+
140
+ function handleThreadUpdated(thread: any) {
141
+ console.log('Thread updated:', thread.id);
142
+ console.log('Title:', thread.title);
143
+ }
144
+
145
+ function handleThreadDeleted(thread: any) {
146
+ console.log('Thread deleted:', thread.id);
147
+ }
148
+
149
+ function handlePostCreated(post: any) {
150
+ console.log('New post created:', post.id);
151
+ console.log('Thread:', post.threadId);
152
+ console.log('Author:', post.userId);
153
+
154
+ // Example: Check for spam, auto-moderate, etc.
155
+ }
156
+
157
+ // Main execution
158
+ async function main() {
159
+ try {
160
+ // Setup webhooks
161
+ const secret = await setupWebhooks();
162
+
163
+ // Start webhook handler server
164
+ // setupWebhookHandler(secret);
165
+
166
+ console.log('\n=== Webhook Setup Complete ===');
167
+ console.log('Remember to save your webhook secret securely!');
168
+
169
+ } catch (error) {
170
+ console.error('Error setting up webhooks:', error);
171
+ }
172
+ }
173
+
174
+ // For demonstration, we'll just setup webhooks
175
+ // Uncomment the line above to also start the webhook handler server
176
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foru-ms/sdk",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "JavaScript SDK for Foru.ms",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/Client.ts CHANGED
@@ -12,14 +12,58 @@ import { PrivateMessagesResource } from './resources/PrivateMessages';
12
12
  import { ReportsResource } from './resources/Reports';
13
13
  import { RolesResource } from './resources/Roles';
14
14
  import { SSOResource } from './resources/SSO';
15
+ import {
16
+ ForumAPIError,
17
+ AuthenticationError,
18
+ AuthorizationError,
19
+ NotFoundError,
20
+ ValidationError,
21
+ RateLimitError,
22
+ ServerError,
23
+ NetworkError
24
+ } from './errors';
25
+ import { PaginationHelper, RetryHelper } from './utils';
26
+ import { RateLimitInfo } from './response-types';
15
27
 
16
28
  // Polyfill fetch if needed (e.g. older Node versions)
17
29
  const fetch = globalThis.fetch || require('cross-fetch');
18
30
 
31
+ export interface ClientOptions {
32
+ /** API key for authentication */
33
+ apiKey: string;
34
+ /** Base URL for the API (default: https://api.foru.ms/v1) */
35
+ baseUrl?: string;
36
+ /** Maximum number of retry attempts for failed requests (default: 3) */
37
+ maxRetries?: number;
38
+ /** Enable automatic retry for rate limits and server errors (default: true) */
39
+ enableRetry?: boolean;
40
+ }
41
+
42
+ /**
43
+ * Main client for interacting with the Foru.ms API
44
+ * @example
45
+ * ```typescript
46
+ * const client = new ForumClient({
47
+ * apiKey: 'your_api_key',
48
+ * baseUrl: 'https://api.foru.ms/v1'
49
+ * });
50
+ *
51
+ * // Set user token
52
+ * client.setToken('user_jwt_token');
53
+ *
54
+ * // Use resources
55
+ * const threads = await client.threads.list();
56
+ * ```
57
+ */
19
58
  export class ForumClient {
20
59
  public apiKey: string;
21
60
  public token: string | null = null;
22
61
  public baseUrl: string;
62
+ public maxRetries: number;
63
+ public enableRetry: boolean;
64
+
65
+ /** Last known rate limit info */
66
+ public lastRateLimitInfo?: RateLimitInfo;
23
67
 
24
68
  public auth: AuthResource;
25
69
  public threads: ThreadsResource;
@@ -36,9 +80,14 @@ export class ForumClient {
36
80
  public roles: RolesResource;
37
81
  public sso: SSOResource;
38
82
 
39
- constructor(options: { apiKey: string; baseUrl?: string }) {
83
+ /** Pagination helper for auto-paginating through results */
84
+ public pagination: PaginationHelper;
85
+
86
+ constructor(options: ClientOptions) {
40
87
  this.apiKey = options.apiKey;
41
88
  this.baseUrl = options.baseUrl || 'https://api.foru.ms/v1';
89
+ this.maxRetries = options.maxRetries ?? 3;
90
+ this.enableRetry = options.enableRetry ?? true;
42
91
 
43
92
  this.auth = new AuthResource(this);
44
93
  this.threads = new ThreadsResource(this);
@@ -54,40 +103,131 @@ export class ForumClient {
54
103
  this.reports = new ReportsResource(this);
55
104
  this.roles = new RolesResource(this);
56
105
  this.sso = new SSOResource(this);
106
+
107
+ this.pagination = new PaginationHelper();
57
108
  }
58
109
 
110
+ /**
111
+ * Make an HTTP request to the API
112
+ * @param path - API endpoint path
113
+ * @param options - Fetch options
114
+ * @returns Promise resolving to the response data
115
+ * @throws {ForumAPIError} When the API returns an error
116
+ * @throws {NetworkError} When the network request fails
117
+ */
59
118
  public async request<T>(path: string, options: RequestInit = {}): Promise<T> {
60
- const headers: Record<string, string> = {
61
- 'Content-Type': 'application/json',
62
- 'x-api-key': this.apiKey,
63
- ...(options.headers as Record<string, string>),
64
- };
119
+ const makeRequest = async (): Promise<T> => {
120
+ const headers: Record<string, string> = {
121
+ 'Content-Type': 'application/json',
122
+ 'x-api-key': this.apiKey,
123
+ ...(options.headers as Record<string, string>),
124
+ };
65
125
 
66
- if (this.token) {
67
- headers['Authorization'] = `Bearer ${this.token}`;
68
- }
126
+ if (this.token) {
127
+ headers['Authorization'] = `Bearer ${this.token}`;
128
+ }
69
129
 
70
- const response = await fetch(`${this.baseUrl}${path}`, {
71
- ...options,
72
- headers,
73
- });
74
-
75
- const contentType = response.headers.get('content-type');
76
- let data;
77
- if (contentType && contentType.includes('application/json')) {
78
- data = await response.json();
79
- } else {
80
- data = await response.text();
81
- }
130
+ let response: Response;
131
+
132
+ try {
133
+ response = await fetch(`${this.baseUrl}${path}`, {
134
+ ...options,
135
+ headers,
136
+ });
137
+ } catch (error: any) {
138
+ throw new NetworkError('Network request failed', error);
139
+ }
140
+
141
+ // Extract rate limit info
142
+ this.extractRateLimitInfo(response);
143
+
144
+ const contentType = response.headers.get('content-type');
145
+ let data;
146
+ if (contentType && contentType.includes('application/json')) {
147
+ data = await response.json();
148
+ } else {
149
+ data = await response.text();
150
+ }
151
+
152
+ if (!response.ok) {
153
+ this.handleErrorResponse(response.status, data);
154
+ }
155
+
156
+ return data as T;
157
+ };
82
158
 
83
- if (!response.ok) {
84
- throw new Error((data && (data as any).message) || (data as any).error || 'API Error');
159
+ // Apply retry logic if enabled
160
+ if (this.enableRetry) {
161
+ return RetryHelper.withRetry(makeRequest, this.maxRetries);
85
162
  }
86
163
 
87
- return data as T;
164
+ return makeRequest();
88
165
  }
89
166
 
90
- public setToken(token: string) {
167
+ /**
168
+ * Set the authentication token for user-scoped requests
169
+ * @param token - JWT token
170
+ */
171
+ public setToken(token: string): void {
91
172
  this.token = token;
92
173
  }
174
+
175
+ /**
176
+ * Clear the authentication token
177
+ */
178
+ public clearToken(): void {
179
+ this.token = null;
180
+ }
181
+
182
+ /**
183
+ * Check if client is authenticated
184
+ */
185
+ public isAuthenticated(): boolean {
186
+ return this.token !== null;
187
+ }
188
+
189
+ /**
190
+ * Extract and store rate limit information from response headers
191
+ */
192
+ private extractRateLimitInfo(response: Response): void {
193
+ const limit = response.headers.get('x-ratelimit-limit');
194
+ const remaining = response.headers.get('x-ratelimit-remaining');
195
+ const reset = response.headers.get('x-ratelimit-reset');
196
+
197
+ if (limit && remaining && reset) {
198
+ this.lastRateLimitInfo = {
199
+ limit: parseInt(limit, 10),
200
+ remaining: parseInt(remaining, 10),
201
+ reset: parseInt(reset, 10),
202
+ };
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Handle error responses by throwing appropriate error types
208
+ */
209
+ private handleErrorResponse(status: number, data: any): never {
210
+ const message = (data && data.message) || data.error || 'API Error';
211
+
212
+ switch (status) {
213
+ case 401:
214
+ throw new AuthenticationError(message, data);
215
+ case 403:
216
+ throw new AuthorizationError(message, data);
217
+ case 404:
218
+ throw new NotFoundError(message, data);
219
+ case 422:
220
+ throw new ValidationError(message, data);
221
+ case 429:
222
+ const retryAfter = data.retryAfter || 60;
223
+ throw new RateLimitError(message, retryAfter, data);
224
+ case 500:
225
+ case 502:
226
+ case 503:
227
+ case 504:
228
+ throw new ServerError(message, status, data);
229
+ default:
230
+ throw new ForumAPIError(message, status, data);
231
+ }
232
+ }
93
233
  }
package/src/errors.ts ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Base error class for all Foru.ms API errors
3
+ */
4
+ export class ForumAPIError extends Error {
5
+ constructor(
6
+ message: string,
7
+ public statusCode: number,
8
+ public response?: any
9
+ ) {
10
+ super(message);
11
+ this.name = 'ForumAPIError';
12
+ Object.setPrototypeOf(this, ForumAPIError.prototype);
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Error thrown when authentication fails or token is invalid
18
+ */
19
+ export class AuthenticationError extends ForumAPIError {
20
+ constructor(message: string = 'Authentication failed', response?: any) {
21
+ super(message, 401, response);
22
+ this.name = 'AuthenticationError';
23
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Error thrown when user doesn't have permission for an action
29
+ */
30
+ export class AuthorizationError extends ForumAPIError {
31
+ constructor(message: string = 'Permission denied', response?: any) {
32
+ super(message, 403, response);
33
+ this.name = 'AuthorizationError';
34
+ Object.setPrototypeOf(this, AuthorizationError.prototype);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Error thrown when a requested resource is not found
40
+ */
41
+ export class NotFoundError extends ForumAPIError {
42
+ constructor(message: string = 'Resource not found', response?: any) {
43
+ super(message, 404, response);
44
+ this.name = 'NotFoundError';
45
+ Object.setPrototypeOf(this, NotFoundError.prototype);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Error thrown when request validation fails
51
+ */
52
+ export class ValidationError extends ForumAPIError {
53
+ constructor(message: string = 'Validation failed', response?: any) {
54
+ super(message, 422, response);
55
+ this.name = 'ValidationError';
56
+ Object.setPrototypeOf(this, ValidationError.prototype);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Error thrown when rate limit is exceeded
62
+ */
63
+ export class RateLimitError extends ForumAPIError {
64
+ constructor(
65
+ message: string = 'Rate limit exceeded',
66
+ public retryAfter?: number,
67
+ response?: any
68
+ ) {
69
+ super(message, 429, response);
70
+ this.name = 'RateLimitError';
71
+ Object.setPrototypeOf(this, RateLimitError.prototype);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Error thrown when server encounters an error
77
+ */
78
+ export class ServerError extends ForumAPIError {
79
+ constructor(message: string = 'Server error', statusCode: number = 500, response?: any) {
80
+ super(message, statusCode, response);
81
+ this.name = 'ServerError';
82
+ Object.setPrototypeOf(this, ServerError.prototype);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Error thrown when network request fails
88
+ */
89
+ export class NetworkError extends Error {
90
+ constructor(message: string = 'Network request failed', public cause?: Error) {
91
+ super(message);
92
+ this.name = 'NetworkError';
93
+ Object.setPrototypeOf(this, NetworkError.prototype);
94
+ }
95
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export * from './Client';
2
2
  export * from './types';
3
+ export * from './errors';
4
+ export * from './response-types';
5
+ export * from './utils';
3
6
  export * from './resources/Auth';
4
7
  export * from './resources/Threads';
5
8
  export * from './resources/Posts';
@@ -31,6 +31,17 @@ export class IntegrationsResource {
31
31
  });
32
32
  }
33
33
 
34
+ async update(id: string, payload: {
35
+ name?: string;
36
+ config?: any;
37
+ active?: boolean;
38
+ }): Promise<{ integration: Integration }> {
39
+ return this.client.request<{ integration: Integration }>(`/integrations/${id}`, {
40
+ method: 'PATCH',
41
+ body: JSON.stringify(payload),
42
+ });
43
+ }
44
+
34
45
  async delete(id: string): Promise<{ success: boolean }> {
35
46
  return this.client.request<{ success: boolean }>(`/integrations/${id}`, {
36
47
  method: 'DELETE',
@@ -84,6 +84,20 @@ export class PostsResource {
84
84
  });
85
85
  }
86
86
 
87
+ async getLikes(id: string, params?: {
88
+ cursor?: string;
89
+ }): Promise<any> {
90
+ const searchParams = new URLSearchParams();
91
+ if (params) {
92
+ Object.entries(params).forEach(([key, value]) => {
93
+ if (value !== undefined) {
94
+ searchParams.append(key, value as string);
95
+ }
96
+ });
97
+ }
98
+ return this.client.request(`/post/${id}/likes?${searchParams.toString()}`, { method: 'GET' });
99
+ }
100
+
87
101
  async dislike(id: string, userId?: string, extendedData?: any): Promise<any> {
88
102
  return this.client.request(`/post/${id}/dislikes`, {
89
103
  method: 'POST',
@@ -97,6 +111,20 @@ export class PostsResource {
97
111
  });
98
112
  }
99
113
 
114
+ async getDislikes(id: string, params?: {
115
+ cursor?: string;
116
+ }): Promise<any> {
117
+ const searchParams = new URLSearchParams();
118
+ if (params) {
119
+ Object.entries(params).forEach(([key, value]) => {
120
+ if (value !== undefined) {
121
+ searchParams.append(key, value as string);
122
+ }
123
+ });
124
+ }
125
+ return this.client.request(`/post/${id}/dislikes?${searchParams.toString()}`, { method: 'GET' });
126
+ }
127
+
100
128
  async upvote(id: string, userId?: string, extendedData?: any): Promise<any> {
101
129
  return this.client.request(`/post/${id}/upvotes`, {
102
130
  method: 'POST',
@@ -110,6 +138,20 @@ export class PostsResource {
110
138
  });
111
139
  }
112
140
 
141
+ async getUpvotes(id: string, params?: {
142
+ cursor?: string;
143
+ }): Promise<any> {
144
+ const searchParams = new URLSearchParams();
145
+ if (params) {
146
+ Object.entries(params).forEach(([key, value]) => {
147
+ if (value !== undefined) {
148
+ searchParams.append(key, value as string);
149
+ }
150
+ });
151
+ }
152
+ return this.client.request(`/post/${id}/upvotes?${searchParams.toString()}`, { method: 'GET' });
153
+ }
154
+
113
155
  async downvote(id: string, userId?: string, extendedData?: any): Promise<any> {
114
156
  return this.client.request(`/post/${id}/downvotes`, {
115
157
  method: 'POST',
@@ -122,4 +164,18 @@ export class PostsResource {
122
164
  method: 'DELETE',
123
165
  });
124
166
  }
167
+
168
+ async getDownvotes(id: string, params?: {
169
+ cursor?: string;
170
+ }): Promise<any> {
171
+ const searchParams = new URLSearchParams();
172
+ if (params) {
173
+ Object.entries(params).forEach(([key, value]) => {
174
+ if (value !== undefined) {
175
+ searchParams.append(key, value as string);
176
+ }
177
+ });
178
+ }
179
+ return this.client.request(`/post/${id}/downvotes?${searchParams.toString()}`, { method: 'GET' });
180
+ }
125
181
  }
@@ -59,6 +59,16 @@ export class PrivateMessagesResource {
59
59
  });
60
60
  }
61
61
 
62
+ async update(id: string, payload: {
63
+ read?: boolean;
64
+ extendedData?: Record<string, any>;
65
+ }): Promise<PrivateMessage> {
66
+ return this.client.request<PrivateMessage>(`/private-message/${id}`, {
67
+ method: 'PATCH',
68
+ body: JSON.stringify(payload),
69
+ });
70
+ }
71
+
62
72
  async delete(id: string): Promise<PrivateMessage & { deleted: boolean }> {
63
73
  return this.client.request<PrivateMessage & { deleted: boolean }>(`/private-message/${id}`, {
64
74
  method: 'DELETE',
@@ -25,6 +25,23 @@ export class SSOResource {
25
25
  });
26
26
  }
27
27
 
28
+ async retrieve(id: string): Promise<{ ssoProvider: SSOProvider }> {
29
+ return this.client.request<{ ssoProvider: SSOProvider }>(`/sso/${id}`, {
30
+ method: 'GET',
31
+ });
32
+ }
33
+
34
+ async update(id: string, payload: {
35
+ domain?: string;
36
+ config?: any;
37
+ active?: boolean;
38
+ }): Promise<{ ssoProvider: SSOProvider }> {
39
+ return this.client.request<{ ssoProvider: SSOProvider }>(`/sso/${id}`, {
40
+ method: 'PATCH',
41
+ body: JSON.stringify(payload),
42
+ });
43
+ }
44
+
28
45
  async delete(id: string): Promise<{ success: boolean }> {
29
46
  return this.client.request<{ success: boolean }>(`/sso/${id}`, {
30
47
  method: 'DELETE',