@rooguys/sdk 0.1.0 → 1.0.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 +478 -113
- package/dist/__tests__/utils/mockClient.d.ts +65 -3
- package/dist/__tests__/utils/mockClient.js +144 -5
- package/dist/errors.d.ts +123 -0
- package/dist/errors.js +163 -0
- package/dist/http-client.d.ts +167 -0
- package/dist/http-client.js +250 -0
- package/dist/index.d.ts +160 -10
- package/dist/index.js +585 -146
- package/dist/types.d.ts +372 -50
- package/dist/types.js +21 -0
- package/package.json +1 -1
- package/src/__tests__/property/request-construction.property.test.ts +142 -91
- package/src/__tests__/property/response-parsing.property.test.ts +118 -67
- package/src/__tests__/property/sdk-modules.property.test.ts +450 -0
- package/src/__tests__/unit/aha.test.ts +61 -50
- package/src/__tests__/unit/badges.test.ts +27 -33
- package/src/__tests__/unit/config.test.ts +94 -126
- package/src/__tests__/unit/errors.test.ts +106 -150
- package/src/__tests__/unit/events.test.ts +119 -144
- package/src/__tests__/unit/leaderboards.test.ts +173 -40
- package/src/__tests__/unit/levels.test.ts +25 -33
- package/src/__tests__/unit/questionnaires.test.ts +33 -42
- package/src/__tests__/unit/users.test.ts +214 -99
- package/src/__tests__/utils/mockClient.ts +193 -6
- package/src/errors.ts +255 -0
- package/src/http-client.ts +433 -0
- package/src/index.ts +742 -150
- package/src/types.ts +429 -51
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rooguys Node.js SDK
|
|
2
2
|
|
|
3
|
-
The official Node.js SDK for the Rooguys Gamification API.
|
|
3
|
+
The official Node.js SDK for the Rooguys Gamification API with full TypeScript support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -11,207 +11,572 @@ npm install @rooguys/sdk
|
|
|
11
11
|
## Initialization
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { Rooguys } from '@rooguys/sdk';
|
|
14
|
+
import { Rooguys, RooguysOptions } from '@rooguys/sdk';
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
baseUrl: 'https://api.rooguys.com/v1', // Optional
|
|
16
|
+
const options: RooguysOptions = {
|
|
17
|
+
baseUrl: 'https://api.rooguys.com/v1', // Optional
|
|
18
18
|
timeout: 10000, // Optional, defaults to 10s
|
|
19
|
-
|
|
19
|
+
// Rate limit handling
|
|
20
|
+
onRateLimitWarning: (info) => {
|
|
21
|
+
console.warn(`Rate limit: ${info.remaining}/${info.limit} remaining`);
|
|
22
|
+
},
|
|
23
|
+
autoRetry: true,
|
|
24
|
+
maxRetries: 3,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const client = new Rooguys('YOUR_API_KEY', options);
|
|
20
28
|
```
|
|
21
29
|
|
|
30
|
+
## Migration Guide (v1.x to v2.x)
|
|
31
|
+
|
|
32
|
+
### Breaking Changes
|
|
33
|
+
|
|
34
|
+
1. **Event Tracking Endpoint**: The SDK now uses `/v1/events` instead of `/v1/event`
|
|
35
|
+
```typescript
|
|
36
|
+
// Old (deprecated, still works with warning)
|
|
37
|
+
await client.events.trackLegacy('event_name', 'user_id', properties);
|
|
38
|
+
|
|
39
|
+
// New (recommended)
|
|
40
|
+
await client.events.track('event-name', 'user_id', properties);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. **Global Leaderboard Endpoint**: Now uses `/v1/leaderboards/global` with `timeframe` query parameter
|
|
44
|
+
```typescript
|
|
45
|
+
// Both signatures work
|
|
46
|
+
await client.leaderboards.getGlobal('weekly', 1, 10);
|
|
47
|
+
await client.leaderboards.getGlobal({ timeframe: 'weekly', page: 1, limit: 10 });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. **Response Format**: All responses now follow standardized format `{ success: true, data: {...} }`
|
|
51
|
+
|
|
52
|
+
### New Features
|
|
53
|
+
|
|
54
|
+
- Batch event tracking (`events.trackBatch`)
|
|
55
|
+
- User management (`users.create`, `users.update`, `users.createBatch`)
|
|
56
|
+
- User search (`users.search`)
|
|
57
|
+
- Field selection for user profiles
|
|
58
|
+
- Leaderboard filters (persona, level range, date range)
|
|
59
|
+
- "Around me" leaderboard view (`leaderboards.getAroundUser`)
|
|
60
|
+
- Health check endpoints
|
|
61
|
+
- Rate limit handling with auto-retry
|
|
62
|
+
- Full TypeScript type definitions
|
|
63
|
+
|
|
22
64
|
## Usage Examples
|
|
23
65
|
|
|
24
|
-
###
|
|
66
|
+
### Events
|
|
25
67
|
|
|
26
|
-
Track
|
|
68
|
+
#### Track a Single Event
|
|
27
69
|
|
|
28
70
|
```typescript
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
import { TrackEventResponse, TrackOptions } from '@rooguys/sdk';
|
|
72
|
+
|
|
73
|
+
const options: TrackOptions = {
|
|
74
|
+
includeProfile: true,
|
|
75
|
+
idempotencyKey: 'unique-request-id'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const response: TrackEventResponse = await client.events.track(
|
|
79
|
+
'level-completed',
|
|
80
|
+
'user_123',
|
|
81
|
+
{ difficulty: 'hard', score: 1500 },
|
|
82
|
+
options
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
console.log(`Event status: ${response.status}`);
|
|
86
|
+
if (response.profile) {
|
|
87
|
+
console.log(`Updated points: ${response.profile.points}`);
|
|
43
88
|
}
|
|
44
89
|
```
|
|
45
90
|
|
|
46
|
-
|
|
91
|
+
#### Track Events with Custom Timestamp
|
|
47
92
|
|
|
48
|
-
|
|
93
|
+
```typescript
|
|
94
|
+
// Track historical events (up to 7 days in the past)
|
|
95
|
+
const response = await client.events.track(
|
|
96
|
+
'purchase',
|
|
97
|
+
'user_123',
|
|
98
|
+
{ amount: 99.99 },
|
|
99
|
+
{ timestamp: new Date('2024-01-15T10:30:00Z') }
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Batch Event Tracking
|
|
49
104
|
|
|
50
105
|
```typescript
|
|
51
|
-
|
|
106
|
+
import { BatchEvent, BatchTrackResponse, BatchOptions } from '@rooguys/sdk';
|
|
107
|
+
|
|
108
|
+
const events: BatchEvent[] = [
|
|
109
|
+
{ eventName: 'page-view', userId: 'user_123', properties: { page: '/home' } },
|
|
110
|
+
{ eventName: 'button-click', userId: 'user_123', properties: { button: 'signup' } },
|
|
111
|
+
{ eventName: 'purchase', userId: 'user_456', properties: { amount: 50 }, timestamp: new Date() }
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const options: BatchOptions = { idempotencyKey: 'batch-123' };
|
|
115
|
+
const response: BatchTrackResponse = await client.events.trackBatch(events, options);
|
|
116
|
+
|
|
117
|
+
// Check individual results
|
|
118
|
+
response.results.forEach((result, index) => {
|
|
119
|
+
if (result.status === 'queued') {
|
|
120
|
+
console.log(`Event ${index} queued successfully`);
|
|
121
|
+
} else {
|
|
122
|
+
console.error(`Event ${index} failed:`, result.error);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Users
|
|
128
|
+
|
|
129
|
+
#### Create a New User
|
|
52
130
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
131
|
+
```typescript
|
|
132
|
+
import { CreateUserData, UserProfile } from '@rooguys/sdk';
|
|
133
|
+
|
|
134
|
+
const userData: CreateUserData = {
|
|
135
|
+
userId: 'user_123',
|
|
136
|
+
displayName: 'John Doe',
|
|
137
|
+
email: 'john@example.com',
|
|
138
|
+
firstName: 'John',
|
|
139
|
+
lastName: 'Doe',
|
|
140
|
+
metadata: { plan: 'premium' }
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const user: UserProfile = await client.users.create(userData);
|
|
57
144
|
```
|
|
58
145
|
|
|
59
|
-
|
|
146
|
+
#### Update User Profile
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { UpdateUserData, UserProfile } from '@rooguys/sdk';
|
|
150
|
+
|
|
151
|
+
// Partial update - only sends provided fields
|
|
152
|
+
const updateData: UpdateUserData = {
|
|
153
|
+
displayName: 'Johnny Doe',
|
|
154
|
+
metadata: { plan: 'enterprise' }
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const updated: UserProfile = await client.users.update('user_123', updateData);
|
|
158
|
+
```
|
|
60
159
|
|
|
61
|
-
|
|
160
|
+
#### Batch User Creation
|
|
62
161
|
|
|
63
162
|
```typescript
|
|
64
|
-
|
|
65
|
-
const leaderboard = await client.leaderboards.getGlobal('all-time', 1, 10);
|
|
163
|
+
import { CreateUserData, BatchCreateResponse } from '@rooguys/sdk';
|
|
66
164
|
|
|
67
|
-
|
|
68
|
-
|
|
165
|
+
const users: CreateUserData[] = [
|
|
166
|
+
{ userId: 'user_1', displayName: 'User One', email: 'one@example.com' },
|
|
167
|
+
{ userId: 'user_2', displayName: 'User Two', email: 'two@example.com' },
|
|
168
|
+
// ... up to 100 users
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const response: BatchCreateResponse = await client.users.createBatch(users);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Get User Profile with Field Selection
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { GetUserOptions, UserProfile } from '@rooguys/sdk';
|
|
178
|
+
|
|
179
|
+
const options: GetUserOptions = {
|
|
180
|
+
fields: ['points', 'level', 'badges']
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const user: UserProfile = await client.users.get('user_123', options);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Search Users
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { SearchOptions, PaginatedResponse, UserProfile } from '@rooguys/sdk';
|
|
190
|
+
|
|
191
|
+
const options: SearchOptions = {
|
|
192
|
+
page: 1,
|
|
193
|
+
limit: 20,
|
|
194
|
+
fields: ['userId', 'displayName', 'points']
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const results: PaginatedResponse<UserProfile> = await client.users.search('john', options);
|
|
198
|
+
|
|
199
|
+
results.users?.forEach(user => {
|
|
200
|
+
console.log(`${user.display_name}: ${user.points} points`);
|
|
69
201
|
});
|
|
70
202
|
```
|
|
71
203
|
|
|
72
|
-
|
|
204
|
+
#### Access Enhanced Profile Data
|
|
73
205
|
|
|
74
|
-
|
|
206
|
+
```typescript
|
|
207
|
+
const user: UserProfile = await client.users.get('user_123');
|
|
208
|
+
|
|
209
|
+
// Activity summary
|
|
210
|
+
if (user.activitySummary) {
|
|
211
|
+
console.log(`Last active: ${user.activitySummary.lastEventAt}`);
|
|
212
|
+
console.log(`Total events: ${user.activitySummary.eventCount}`);
|
|
213
|
+
console.log(`Days active: ${user.activitySummary.daysActive}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Streak information
|
|
217
|
+
if (user.streak) {
|
|
218
|
+
console.log(`Current streak: ${user.streak.currentStreak} days`);
|
|
219
|
+
console.log(`Longest streak: ${user.streak.longestStreak} days`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Inventory summary
|
|
223
|
+
if (user.inventory) {
|
|
224
|
+
console.log(`Items owned: ${user.inventory.itemCount}`);
|
|
225
|
+
console.log(`Active effects: ${user.inventory.activeEffects.join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Leaderboards
|
|
230
|
+
|
|
231
|
+
#### Global Leaderboard with Filters
|
|
75
232
|
|
|
76
233
|
```typescript
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
234
|
+
import { LeaderboardFilterOptions, LeaderboardResult } from '@rooguys/sdk';
|
|
235
|
+
|
|
236
|
+
const options: LeaderboardFilterOptions = {
|
|
237
|
+
timeframe: 'weekly',
|
|
238
|
+
page: 1,
|
|
239
|
+
limit: 10,
|
|
240
|
+
persona: 'competitor',
|
|
241
|
+
minLevel: 5,
|
|
242
|
+
maxLevel: 20,
|
|
243
|
+
startDate: new Date('2024-01-01'),
|
|
244
|
+
endDate: new Date('2024-01-31')
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const leaderboard: LeaderboardResult = await client.leaderboards.getGlobal(options);
|
|
248
|
+
|
|
249
|
+
// Access cache metadata
|
|
250
|
+
if (leaderboard.cacheMetadata) {
|
|
251
|
+
console.log(`Cached at: ${leaderboard.cacheMetadata.cachedAt}`);
|
|
252
|
+
console.log(`TTL: ${leaderboard.cacheMetadata.ttl}s`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Rankings include percentile
|
|
256
|
+
leaderboard.rankings.forEach(entry => {
|
|
257
|
+
console.log(`#${entry.rank} ${entry.user_id}: ${entry.points} pts (top ${entry.percentile}%)`);
|
|
258
|
+
});
|
|
81
259
|
```
|
|
82
260
|
|
|
83
|
-
|
|
261
|
+
#### Custom Leaderboard with Filters
|
|
84
262
|
|
|
85
|
-
|
|
263
|
+
```typescript
|
|
264
|
+
const customLb: LeaderboardResult = await client.leaderboards.getCustom('leaderboard_id', {
|
|
265
|
+
page: 1,
|
|
266
|
+
limit: 10,
|
|
267
|
+
persona: 'achiever',
|
|
268
|
+
minLevel: 10
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### "Around Me" View
|
|
86
273
|
|
|
87
274
|
```typescript
|
|
88
|
-
|
|
89
|
-
|
|
275
|
+
import { AroundUserResponse } from '@rooguys/sdk';
|
|
276
|
+
|
|
277
|
+
const aroundMe: AroundUserResponse = await client.leaderboards.getAroundUser(
|
|
278
|
+
'leaderboard_id',
|
|
279
|
+
'user_123',
|
|
280
|
+
5 // 5 entries above and below
|
|
281
|
+
);
|
|
90
282
|
|
|
91
|
-
|
|
283
|
+
aroundMe.rankings.forEach(entry => {
|
|
284
|
+
const marker = entry.user_id === 'user_123' ? '→' : ' ';
|
|
285
|
+
console.log(`${marker} #${entry.rank} ${entry.user_id}: ${entry.points}`);
|
|
286
|
+
});
|
|
92
287
|
```
|
|
93
288
|
|
|
94
|
-
|
|
289
|
+
#### Get User Rank with Percentile
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { UserRank } from '@rooguys/sdk';
|
|
293
|
+
|
|
294
|
+
const rank: UserRank = await client.leaderboards.getUserRank('leaderboard_id', 'user_123');
|
|
295
|
+
|
|
296
|
+
console.log(`Rank: #${rank.rank}`);
|
|
297
|
+
console.log(`Score: ${rank.points}`);
|
|
298
|
+
console.log(`Percentile: top ${rank.percentile}%`);
|
|
299
|
+
```
|
|
95
300
|
|
|
96
|
-
|
|
301
|
+
### Health Checks
|
|
97
302
|
|
|
98
303
|
```typescript
|
|
99
|
-
|
|
304
|
+
import { HealthCheckResponse } from '@rooguys/sdk';
|
|
100
305
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log(`
|
|
104
|
-
console.log(`
|
|
306
|
+
// Full health check
|
|
307
|
+
const health: HealthCheckResponse = await client.health.check();
|
|
308
|
+
console.log(`Status: ${health.status}`);
|
|
309
|
+
console.log(`Version: ${health.version}`);
|
|
105
310
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
console.log(
|
|
311
|
+
// Quick availability check
|
|
312
|
+
const isReady: boolean = await client.health.isReady();
|
|
313
|
+
if (isReady) {
|
|
314
|
+
console.log('API is ready');
|
|
110
315
|
}
|
|
111
316
|
```
|
|
112
317
|
|
|
318
|
+
### Aha Score
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { AhaDeclarationResult, AhaScoreResult } from '@rooguys/sdk';
|
|
322
|
+
|
|
323
|
+
// Declare user activation milestone (1-5)
|
|
324
|
+
const result: AhaDeclarationResult = await client.aha.declare('user_123', 4);
|
|
325
|
+
console.log(result.message);
|
|
326
|
+
|
|
327
|
+
// Get user's aha score
|
|
328
|
+
const score: AhaScoreResult = await client.aha.getUserScore('user_123');
|
|
329
|
+
console.log(`Current Score: ${score.data.current_score}`);
|
|
330
|
+
console.log(`Status: ${score.data.status}`);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## TypeScript Types
|
|
334
|
+
|
|
335
|
+
The SDK exports comprehensive TypeScript types for all operations:
|
|
336
|
+
|
|
337
|
+
### Core Types
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import {
|
|
341
|
+
// SDK Options
|
|
342
|
+
RooguysOptions,
|
|
343
|
+
|
|
344
|
+
// User Types
|
|
345
|
+
UserProfile,
|
|
346
|
+
CreateUserData,
|
|
347
|
+
UpdateUserData,
|
|
348
|
+
GetUserOptions,
|
|
349
|
+
SearchOptions,
|
|
350
|
+
ActivitySummary,
|
|
351
|
+
StreakInfo,
|
|
352
|
+
InventorySummary,
|
|
353
|
+
|
|
354
|
+
// Event Types
|
|
355
|
+
TrackEventResponse,
|
|
356
|
+
TrackOptions,
|
|
357
|
+
BatchEvent,
|
|
358
|
+
BatchTrackResponse,
|
|
359
|
+
BatchOptions,
|
|
360
|
+
|
|
361
|
+
// Leaderboard Types
|
|
362
|
+
LeaderboardResult,
|
|
363
|
+
LeaderboardFilterOptions,
|
|
364
|
+
LeaderboardEntry,
|
|
365
|
+
AroundUserResponse,
|
|
366
|
+
UserRank,
|
|
367
|
+
Timeframe,
|
|
368
|
+
|
|
369
|
+
// Batch Types
|
|
370
|
+
BatchCreateResponse,
|
|
371
|
+
PaginatedResponse,
|
|
372
|
+
|
|
373
|
+
// Other Types
|
|
374
|
+
Badge,
|
|
375
|
+
Level,
|
|
376
|
+
Questionnaire,
|
|
377
|
+
HealthCheckResponse,
|
|
378
|
+
AhaDeclarationResult,
|
|
379
|
+
AhaScoreResult,
|
|
380
|
+
} from '@rooguys/sdk';
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### HTTP Client Types
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
import {
|
|
387
|
+
RateLimitInfo,
|
|
388
|
+
CacheMetadata,
|
|
389
|
+
Pagination,
|
|
390
|
+
ApiResponse,
|
|
391
|
+
RequestConfig,
|
|
392
|
+
HttpClientOptions,
|
|
393
|
+
} from '@rooguys/sdk';
|
|
394
|
+
```
|
|
395
|
+
|
|
113
396
|
## API Reference
|
|
114
397
|
|
|
115
398
|
### Events
|
|
116
399
|
|
|
117
|
-
|
|
400
|
+
| Method | Return Type | Description |
|
|
401
|
+
|--------|-------------|-------------|
|
|
402
|
+
| `track(eventName, userId, properties?, options?)` | `Promise<TrackEventResponse>` | Track a single event |
|
|
403
|
+
| `trackBatch(events, options?)` | `Promise<BatchTrackResponse>` | Track multiple events (max 100) |
|
|
404
|
+
| `trackLegacy(eventName, userId, properties?, options?)` | `Promise<TrackEventResponse>` | **Deprecated** |
|
|
118
405
|
|
|
119
406
|
### Users
|
|
120
407
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
408
|
+
| Method | Return Type | Description |
|
|
409
|
+
|--------|-------------|-------------|
|
|
410
|
+
| `create(userData)` | `Promise<UserProfile>` | Create a new user |
|
|
411
|
+
| `update(userId, userData)` | `Promise<UserProfile>` | Update user profile |
|
|
412
|
+
| `createBatch(users)` | `Promise<BatchCreateResponse>` | Create multiple users (max 100) |
|
|
413
|
+
| `get(userId, options?)` | `Promise<UserProfile>` | Get user profile |
|
|
414
|
+
| `search(query, options?)` | `Promise<PaginatedResponse<UserProfile>>` | Search users |
|
|
415
|
+
| `getBulk(userIds)` | `Promise<{ users: UserProfile[] }>` | Get multiple profiles |
|
|
416
|
+
| `getBadges(userId)` | `Promise<{ badges: UserBadge[] }>` | Get user's badges |
|
|
417
|
+
| `getRank(userId, timeframe?)` | `Promise<UserRank>` | Get user's global rank |
|
|
418
|
+
| `submitAnswers(userId, questionnaireId, answers)` | `Promise<{ status, message }>` | Submit answers |
|
|
126
419
|
|
|
127
420
|
### Leaderboards
|
|
128
421
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
422
|
+
| Method | Return Type | Description |
|
|
423
|
+
|--------|-------------|-------------|
|
|
424
|
+
| `getGlobal(timeframeOrOptions?, page?, limit?, options?)` | `Promise<LeaderboardResult>` | Get global leaderboard |
|
|
425
|
+
| `list(pageOrOptions?, limit?, search?)` | `Promise<LeaderboardListResult>` | List all leaderboards |
|
|
426
|
+
| `getCustom(leaderboardId, pageOrOptions?, ...)` | `Promise<LeaderboardResult>` | Get custom leaderboard |
|
|
427
|
+
| `getUserRank(leaderboardId, userId)` | `Promise<UserRank>` | Get user's rank |
|
|
428
|
+
| `getAroundUser(leaderboardId, userId, range?)` | `Promise<AroundUserResponse>` | Get entries around user |
|
|
133
429
|
|
|
134
430
|
### Badges
|
|
135
431
|
|
|
136
|
-
|
|
432
|
+
| Method | Return Type | Description |
|
|
433
|
+
|--------|-------------|-------------|
|
|
434
|
+
| `list(page?, limit?, activeOnly?)` | `Promise<BadgeListResult>` | List all badges |
|
|
137
435
|
|
|
138
436
|
### Levels
|
|
139
437
|
|
|
140
|
-
|
|
438
|
+
| Method | Return Type | Description |
|
|
439
|
+
|--------|-------------|-------------|
|
|
440
|
+
| `list(page?, limit?)` | `Promise<LevelListResult>` | List all levels |
|
|
141
441
|
|
|
142
442
|
### Questionnaires
|
|
143
443
|
|
|
144
|
-
|
|
145
|
-
|
|
444
|
+
| Method | Return Type | Description |
|
|
445
|
+
|--------|-------------|-------------|
|
|
446
|
+
| `get(slug)` | `Promise<Questionnaire>` | Get questionnaire by slug |
|
|
447
|
+
| `getActive()` | `Promise<Questionnaire>` | Get active questionnaire |
|
|
146
448
|
|
|
147
449
|
### Aha Score
|
|
148
450
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
451
|
+
| Method | Return Type | Description |
|
|
452
|
+
|--------|-------------|-------------|
|
|
453
|
+
| `declare(userId, value)` | `Promise<AhaDeclarationResult>` | Declare aha score (1-5) |
|
|
454
|
+
| `getUserScore(userId)` | `Promise<AhaScoreResult>` | Get user's aha score |
|
|
455
|
+
|
|
456
|
+
### Health
|
|
457
|
+
|
|
458
|
+
| Method | Return Type | Description |
|
|
459
|
+
|--------|-------------|-------------|
|
|
460
|
+
| `check()` | `Promise<HealthCheckResponse>` | Get full health status |
|
|
461
|
+
| `isReady()` | `Promise<boolean>` | Quick availability check |
|
|
153
462
|
|
|
154
463
|
## Error Handling
|
|
155
464
|
|
|
156
|
-
The SDK
|
|
465
|
+
The SDK provides typed error classes for different error scenarios:
|
|
157
466
|
|
|
158
467
|
```typescript
|
|
468
|
+
import {
|
|
469
|
+
Rooguys,
|
|
470
|
+
ValidationError,
|
|
471
|
+
AuthenticationError,
|
|
472
|
+
NotFoundError,
|
|
473
|
+
ConflictError,
|
|
474
|
+
RateLimitError,
|
|
475
|
+
ServerError,
|
|
476
|
+
FieldError
|
|
477
|
+
} from '@rooguys/sdk';
|
|
478
|
+
|
|
159
479
|
try {
|
|
160
|
-
await client.users.
|
|
480
|
+
await client.users.create({ userId: 'user_123', email: 'invalid-email' });
|
|
161
481
|
} catch (error) {
|
|
162
|
-
|
|
482
|
+
if (error instanceof ValidationError) {
|
|
483
|
+
console.error('Validation failed:', error.message);
|
|
484
|
+
console.error('Field errors:', error.fieldErrors);
|
|
485
|
+
console.error('Error code:', error.code);
|
|
486
|
+
} else if (error instanceof AuthenticationError) {
|
|
487
|
+
console.error('Invalid API key');
|
|
488
|
+
} else if (error instanceof NotFoundError) {
|
|
489
|
+
console.error('Resource not found');
|
|
490
|
+
} else if (error instanceof ConflictError) {
|
|
491
|
+
console.error('Resource already exists');
|
|
492
|
+
} else if (error instanceof RateLimitError) {
|
|
493
|
+
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
|
|
494
|
+
} else if (error instanceof ServerError) {
|
|
495
|
+
console.error('Server error:', error.message);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// All errors include requestId for debugging
|
|
499
|
+
if (error instanceof RooguysError) {
|
|
500
|
+
console.error('Request ID:', error.requestId);
|
|
501
|
+
}
|
|
163
502
|
}
|
|
164
503
|
```
|
|
165
504
|
|
|
166
|
-
###
|
|
505
|
+
### Error Types
|
|
506
|
+
|
|
507
|
+
| Error Class | HTTP Status | Properties |
|
|
508
|
+
|-------------|-------------|------------|
|
|
509
|
+
| `ValidationError` | 400 | `fieldErrors?: FieldError[]` |
|
|
510
|
+
| `AuthenticationError` | 401 | - |
|
|
511
|
+
| `ForbiddenError` | 403 | - |
|
|
512
|
+
| `NotFoundError` | 404 | - |
|
|
513
|
+
| `ConflictError` | 409 | - |
|
|
514
|
+
| `RateLimitError` | 429 | `retryAfter: number` |
|
|
515
|
+
| `ServerError` | 500+ | - |
|
|
516
|
+
|
|
517
|
+
### Common Error Properties
|
|
518
|
+
|
|
519
|
+
All errors extend `RooguysError` and include:
|
|
520
|
+
- `message: string` - Human-readable error message
|
|
521
|
+
- `code: string` - Machine-readable error code
|
|
522
|
+
- `requestId?: string` - Unique request identifier
|
|
523
|
+
- `statusCode: number` - HTTP status code
|
|
524
|
+
|
|
525
|
+
### FieldError Type
|
|
167
526
|
|
|
168
527
|
```typescript
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
} catch (error) {
|
|
173
|
-
console.error(error.message); // "Aha score value must be between 1 and 5"
|
|
528
|
+
interface FieldError {
|
|
529
|
+
field: string;
|
|
530
|
+
message: string;
|
|
174
531
|
}
|
|
175
532
|
```
|
|
176
533
|
|
|
177
|
-
##
|
|
534
|
+
## Rate Limiting
|
|
178
535
|
|
|
179
|
-
The SDK
|
|
536
|
+
The SDK provides built-in rate limit handling:
|
|
180
537
|
|
|
181
|
-
|
|
538
|
+
```typescript
|
|
539
|
+
const client = new Rooguys('YOUR_API_KEY', {
|
|
540
|
+
// Get notified when 80% of rate limit is consumed
|
|
541
|
+
onRateLimitWarning: (info: RateLimitInfo) => {
|
|
542
|
+
console.warn(`Rate limit: ${info.remaining}/${info.limit} remaining`);
|
|
543
|
+
console.warn(`Resets at: ${new Date(info.reset * 1000)}`);
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
// Automatically retry rate-limited requests
|
|
547
|
+
autoRetry: true,
|
|
548
|
+
maxRetries: 3
|
|
549
|
+
});
|
|
550
|
+
```
|
|
182
551
|
|
|
183
|
-
|
|
184
|
-
|
|
552
|
+
### RateLimitInfo Type
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
interface RateLimitInfo {
|
|
556
|
+
limit: number; // Total requests allowed
|
|
557
|
+
remaining: number; // Requests remaining
|
|
558
|
+
reset: number; // Unix timestamp when limit resets
|
|
559
|
+
}
|
|
185
560
|
```
|
|
186
561
|
|
|
187
|
-
|
|
562
|
+
## Testing
|
|
188
563
|
|
|
189
564
|
```bash
|
|
190
|
-
npm
|
|
565
|
+
npm test # Run all tests
|
|
566
|
+
npm run test:coverage # Run with coverage report
|
|
191
567
|
```
|
|
192
568
|
|
|
193
|
-
The SDK maintains >90% test coverage
|
|
569
|
+
The SDK maintains >90% test coverage with:
|
|
194
570
|
- Unit tests for all API methods
|
|
195
571
|
- Property-based tests using fast-check
|
|
196
|
-
-
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
arbitraries.eventName(),
|
|
208
|
-
arbitraries.userId(),
|
|
209
|
-
async (eventName, userId) => {
|
|
210
|
-
// Test that any valid event name and user ID constructs a valid request
|
|
211
|
-
await client.events.track(eventName, userId);
|
|
212
|
-
// Assertions verify correct HTTP method, URL, headers, and body
|
|
213
|
-
}
|
|
214
|
-
),
|
|
215
|
-
{ numRuns: 100 }
|
|
216
|
-
);
|
|
217
|
-
```
|
|
572
|
+
- TypeScript type validation
|
|
573
|
+
- Error handling tests
|
|
574
|
+
|
|
575
|
+
## Requirements
|
|
576
|
+
|
|
577
|
+
- Node.js >= 18.x
|
|
578
|
+
- TypeScript >= 4.7 (for TypeScript users)
|
|
579
|
+
|
|
580
|
+
## License
|
|
581
|
+
|
|
582
|
+
MIT
|