@foru-ms/sdk 1.1.1 → 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.
- package/README.md +286 -11
- package/dist/Client.d.ts +63 -4
- package/dist/Client.js +124 -22
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +95 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/resources/Integrations.d.ts +7 -0
- package/dist/resources/Integrations.js +6 -0
- package/dist/resources/Posts.d.ts +12 -0
- package/dist/resources/Posts.js +44 -0
- package/dist/resources/PrivateMessages.d.ts +4 -0
- package/dist/resources/PrivateMessages.js +6 -0
- package/dist/resources/SSO.d.ts +10 -0
- package/dist/resources/SSO.js +11 -0
- package/dist/resources/Tags.d.ts +8 -0
- package/dist/resources/Tags.js +24 -0
- package/dist/resources/Threads.d.ts +20 -0
- package/dist/resources/Threads.js +85 -0
- package/dist/resources/Users.d.ts +10 -0
- package/dist/resources/Users.js +26 -0
- package/dist/resources/Webhooks.d.ts +69 -0
- package/dist/resources/Webhooks.js +115 -0
- package/dist/response-types.d.ts +105 -0
- package/dist/response-types.js +2 -0
- package/dist/utils.d.ts +80 -0
- package/dist/utils.js +138 -0
- package/examples/README.md +38 -0
- package/examples/authentication.ts +79 -0
- package/examples/error-handling.ts +133 -0
- package/examples/managing-threads.ts +130 -0
- package/examples/pagination.ts +81 -0
- package/examples/webhooks.ts +176 -0
- package/package.json +1 -1
- package/src/Client.ts +165 -25
- package/src/errors.ts +95 -0
- package/src/index.ts +3 -0
- package/src/resources/Integrations.ts +11 -0
- package/src/resources/Posts.ts +56 -0
- package/src/resources/PrivateMessages.ts +10 -0
- package/src/resources/SSO.ts +17 -0
- package/src/resources/Tags.ts +32 -0
- package/src/resources/Threads.ts +109 -0
- package/src/resources/Users.ts +36 -0
- package/src/resources/Webhooks.ts +131 -0
- package/src/response-types.ts +113 -0
- package/src/utils.ts +182 -0
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
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
126
|
+
if (this.token) {
|
|
127
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
128
|
+
}
|
|
69
129
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
84
|
-
|
|
159
|
+
// Apply retry logic if enabled
|
|
160
|
+
if (this.enableRetry) {
|
|
161
|
+
return RetryHelper.withRetry(makeRequest, this.maxRetries);
|
|
85
162
|
}
|
|
86
163
|
|
|
87
|
-
return
|
|
164
|
+
return makeRequest();
|
|
88
165
|
}
|
|
89
166
|
|
|
90
|
-
|
|
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
|
@@ -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',
|
package/src/resources/Posts.ts
CHANGED
|
@@ -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',
|
package/src/resources/SSO.ts
CHANGED
|
@@ -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',
|
package/src/resources/Tags.ts
CHANGED
|
@@ -64,6 +64,24 @@ export class TagsResource {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
async getThreads(id: string, params?: {
|
|
68
|
+
query?: string;
|
|
69
|
+
cursor?: string;
|
|
70
|
+
filter?: 'newest' | 'oldest';
|
|
71
|
+
}): Promise<import('../types').ThreadListResponse> {
|
|
72
|
+
const searchParams = new URLSearchParams();
|
|
73
|
+
if (params) {
|
|
74
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
searchParams.append(key, value as string);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return this.client.request<import('../types').ThreadListResponse>(`/tag/${id}/threads?${searchParams.toString()}`, {
|
|
81
|
+
method: 'GET',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
async subscribe(id: string, userId: string): Promise<any> {
|
|
68
86
|
return this.client.request(`/tag/${id}/subscribers`, {
|
|
69
87
|
method: 'POST',
|
|
@@ -77,6 +95,20 @@ export class TagsResource {
|
|
|
77
95
|
});
|
|
78
96
|
}
|
|
79
97
|
|
|
98
|
+
async getSubscribers(id: string, params?: {
|
|
99
|
+
cursor?: string;
|
|
100
|
+
}): Promise<any> {
|
|
101
|
+
const searchParams = new URLSearchParams();
|
|
102
|
+
if (params) {
|
|
103
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
104
|
+
if (value !== undefined) {
|
|
105
|
+
searchParams.append(key, value as string);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return this.client.request(`/tag/${id}/subscribers?${searchParams.toString()}`, { method: 'GET' });
|
|
110
|
+
}
|
|
111
|
+
|
|
80
112
|
async listSubscribed(params: {
|
|
81
113
|
userId: string;
|
|
82
114
|
query?: string;
|
package/src/resources/Threads.ts
CHANGED
|
@@ -85,6 +85,20 @@ export class ThreadsResource {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
async getLikes(id: string, params?: {
|
|
89
|
+
cursor?: string;
|
|
90
|
+
}): Promise<any> {
|
|
91
|
+
const searchParams = new URLSearchParams();
|
|
92
|
+
if (params) {
|
|
93
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
94
|
+
if (value !== undefined) {
|
|
95
|
+
searchParams.append(key, value as string);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return this.client.request(`/thread/${id}/likes?${searchParams.toString()}`, { method: 'GET' });
|
|
100
|
+
}
|
|
101
|
+
|
|
88
102
|
async dislike(id: string, userId?: string, extendedData?: any): Promise<any> {
|
|
89
103
|
return this.client.request(`/thread/${id}/dislikes`, {
|
|
90
104
|
method: 'POST',
|
|
@@ -98,6 +112,20 @@ export class ThreadsResource {
|
|
|
98
112
|
});
|
|
99
113
|
}
|
|
100
114
|
|
|
115
|
+
async getDislikes(id: string, params?: {
|
|
116
|
+
cursor?: string;
|
|
117
|
+
}): Promise<any> {
|
|
118
|
+
const searchParams = new URLSearchParams();
|
|
119
|
+
if (params) {
|
|
120
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
121
|
+
if (value !== undefined) {
|
|
122
|
+
searchParams.append(key, value as string);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return this.client.request(`/thread/${id}/dislikes?${searchParams.toString()}`, { method: 'GET' });
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
async subscribe(id: string, userId: string, extendedData?: any): Promise<any> {
|
|
102
130
|
return this.client.request(`/thread/${id}/subscribers`, {
|
|
103
131
|
method: 'POST',
|
|
@@ -111,6 +139,87 @@ export class ThreadsResource {
|
|
|
111
139
|
});
|
|
112
140
|
}
|
|
113
141
|
|
|
142
|
+
async getSubscribers(id: string, params?: {
|
|
143
|
+
cursor?: string;
|
|
144
|
+
}): Promise<any> {
|
|
145
|
+
const searchParams = new URLSearchParams();
|
|
146
|
+
if (params) {
|
|
147
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
148
|
+
if (value !== undefined) {
|
|
149
|
+
searchParams.append(key, value as string);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return this.client.request(`/thread/${id}/subscribers?${searchParams.toString()}`, { method: 'GET' });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async upvote(id: string, userId?: string, extendedData?: any): Promise<any> {
|
|
157
|
+
return this.client.request(`/thread/${id}/upvotes`, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
body: JSON.stringify({ userId, extendedData }),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async unupvote(id: string, userId: string): Promise<any> {
|
|
164
|
+
return this.client.request(`/thread/${id}/upvotes?userId=${userId}`, {
|
|
165
|
+
method: 'DELETE',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async getUpvotes(id: string, params?: {
|
|
170
|
+
cursor?: string;
|
|
171
|
+
}): Promise<any> {
|
|
172
|
+
const searchParams = new URLSearchParams();
|
|
173
|
+
if (params) {
|
|
174
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
175
|
+
if (value !== undefined) {
|
|
176
|
+
searchParams.append(key, value as string);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return this.client.request(`/thread/${id}/upvotes?${searchParams.toString()}`, { method: 'GET' });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async downvote(id: string, userId?: string, extendedData?: any): Promise<any> {
|
|
184
|
+
return this.client.request(`/thread/${id}/downvotes`, {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
body: JSON.stringify({ userId, extendedData }),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async undownvote(id: string, userId: string): Promise<any> {
|
|
191
|
+
return this.client.request(`/thread/${id}/downvotes?userId=${userId}`, {
|
|
192
|
+
method: 'DELETE',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async getDownvotes(id: string, params?: {
|
|
197
|
+
cursor?: string;
|
|
198
|
+
}): Promise<any> {
|
|
199
|
+
const searchParams = new URLSearchParams();
|
|
200
|
+
if (params) {
|
|
201
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
202
|
+
if (value !== undefined) {
|
|
203
|
+
searchParams.append(key, value as string);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return this.client.request(`/thread/${id}/downvotes?${searchParams.toString()}`, { method: 'GET' });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async getPoll(threadId: string, userId?: string): Promise<any> {
|
|
211
|
+
const searchParams = new URLSearchParams();
|
|
212
|
+
if (userId) {
|
|
213
|
+
searchParams.append('userId', userId);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const query = searchParams.toString();
|
|
217
|
+
return this.client.request(
|
|
218
|
+
`/thread/${threadId}/poll${query ? `?${query}` : ''}`,
|
|
219
|
+
{ method: 'GET' }
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
114
223
|
async vote(id: string, optionId: string, userId: string): Promise<any> {
|
|
115
224
|
return this.client.request(`/thread/${id}/poll/votes`, {
|
|
116
225
|
method: 'POST',
|