@ibertran/fortytwo-intra-client 3.1.4 → 4.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 +97 -98
- package/dist/index.d.ts +22 -3
- package/dist/index.js +100 -43
- package/dist/lib/errors.d.ts +8 -0
- package/dist/lib/errors.js +27 -0
- package/dist/lib/logs.js +0 -1
- package/package.json +29 -24
- package/dist/HttpError.d.ts +0 -9
- package/dist/HttpError.js +0 -25
- package/dist/utils.d.ts +0 -4
- package/dist/utils.js +0 -79
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# FortytwoIntraClient
|
|
2
2
|
|
|
3
|
-
A TypeScript library for interacting with the
|
|
3
|
+
A TypeScript library for interacting with the 42 API with built-in rate limiting, automatic token management, and retry logic.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -11,13 +11,12 @@ npm install @ibertran/fortytwo-intra-client
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { FortytwoIntraClient } from
|
|
15
|
-
|
|
16
|
-
const client = new FortytwoIntraClient('your_client_id', 'your_client_secret');
|
|
14
|
+
import { FortytwoIntraClient } from "@ibertran/fortytwo-intra-client";
|
|
17
15
|
|
|
16
|
+
const client = new FortytwoIntraClient("your_client_id", "your_client_secret");
|
|
18
17
|
|
|
19
18
|
// Get a specific user by login
|
|
20
|
-
const user = await client.get(
|
|
19
|
+
const user = await client.get("users/ibertran");
|
|
21
20
|
console.log(user);
|
|
22
21
|
```
|
|
23
22
|
|
|
@@ -26,39 +25,37 @@ console.log(user);
|
|
|
26
25
|
The `FortytwoIntraClient` class accepts three parameters:
|
|
27
26
|
|
|
28
27
|
1. `client_id` (required): Your 42 API client ID
|
|
29
|
-
2. `client_secret` (required): Your 42 API client secret
|
|
28
|
+
2. `client_secret` (required): Your 42 API client secret
|
|
30
29
|
3. `config` (optional): Configuration object
|
|
31
30
|
|
|
32
31
|
### Configuration Options
|
|
33
32
|
|
|
34
33
|
```typescript
|
|
35
34
|
interface FortytwoIntraClientConf {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
throwOnError: boolean; // Throw errors instead of returning them (default: true)
|
|
35
|
+
redirect_uri: string | null; // OAuth redirect URI (default: null)
|
|
36
|
+
base_url: string; // API base URL (default: "https://api.intra.42.fr/v2/")
|
|
37
|
+
token_url: string; // Token endpoint (default: "https://api.intra.42.fr/oauth/token")
|
|
38
|
+
oauth_url: string; // OAuth endpoint (default: "https://api.intra.42.fr/oauth/authorize")
|
|
39
|
+
token_info_url: string; // Token info endpoint (default: "https://api.intra.42.fr/oauth/token/info")
|
|
40
|
+
scopes: string[]; // OAuth scopes (default: ["public"])
|
|
41
|
+
rateLimitMaxRequests: number; // Max requests per time window (default: 2)
|
|
42
|
+
rateLimitPerMilliseconds: number; // Time window in milliseconds (default: 1200)
|
|
43
|
+
maxRetry: number; // Max retry attempts (default: 5)
|
|
44
|
+
logLine: boolean; // Enable request logging (default: true)
|
|
45
|
+
errLogBody: boolean; // Log error response bodies (default: true)
|
|
48
46
|
}
|
|
49
47
|
```
|
|
50
48
|
|
|
51
49
|
### Example with Custom Configuration
|
|
52
50
|
|
|
53
51
|
```typescript
|
|
54
|
-
const client = new FortytwoIntraClient(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
scopes: ['public', 'projects'] // Extended scopes
|
|
52
|
+
const client = new FortytwoIntraClient("your_client_id", "your_client_secret", {
|
|
53
|
+
rateLimitMaxRequests: 1, // 1 request per 1200ms
|
|
54
|
+
rateLimitPerMilliseconds: 1200, // Time window
|
|
55
|
+
maxRetry: 3, // Retry up to 3 times
|
|
56
|
+
logLine: false, // Disable logging
|
|
57
|
+
errLogBody: false, // Disable error body logging
|
|
58
|
+
scopes: ["public", "projects"], // Extended scopes
|
|
62
59
|
});
|
|
63
60
|
```
|
|
64
61
|
|
|
@@ -68,11 +65,11 @@ const client = new FortytwoIntraClient('your_client_id', 'your_client_secret', {
|
|
|
68
65
|
|
|
69
66
|
```typescript
|
|
70
67
|
// Get all campuses
|
|
71
|
-
const campuses = await client.get(
|
|
68
|
+
const campuses = await client.get("campus");
|
|
72
69
|
console.log(`Found ${campuses.length} campuses`);
|
|
73
70
|
|
|
74
71
|
// Get a specific campus
|
|
75
|
-
const lyonCampus = await client.get(
|
|
72
|
+
const lyonCampus = await client.get("campus/9");
|
|
76
73
|
console.log(`${lyonCampus.name}: ${lyonCampus.users_count} users`);
|
|
77
74
|
```
|
|
78
75
|
|
|
@@ -80,11 +77,11 @@ console.log(`${lyonCampus.name}: ${lyonCampus.users_count} users`);
|
|
|
80
77
|
|
|
81
78
|
```typescript
|
|
82
79
|
// Create a team for a project
|
|
83
|
-
const team = await client.post(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
const team = await client.post("teams", {
|
|
81
|
+
body: {
|
|
82
|
+
name: "awesome-team",
|
|
83
|
+
project_id: 42,
|
|
84
|
+
},
|
|
88
85
|
});
|
|
89
86
|
```
|
|
90
87
|
|
|
@@ -92,12 +89,12 @@ const team = await client.post('teams', {
|
|
|
92
89
|
|
|
93
90
|
```typescript
|
|
94
91
|
// Update a team name
|
|
95
|
-
const updatedTeam = await client.patch(
|
|
96
|
-
|
|
92
|
+
const updatedTeam = await client.patch("teams/123", {
|
|
93
|
+
body: {
|
|
97
94
|
team: {
|
|
98
|
-
name: "updated-team-name"
|
|
99
|
-
}
|
|
100
|
-
|
|
95
|
+
name: "updated-team-name",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
101
98
|
});
|
|
102
99
|
```
|
|
103
100
|
|
|
@@ -105,7 +102,7 @@ const updatedTeam = await client.patch('teams/123', {
|
|
|
105
102
|
|
|
106
103
|
```typescript
|
|
107
104
|
// Delete a team
|
|
108
|
-
await client.delete(
|
|
105
|
+
await client.delete("teams/123");
|
|
109
106
|
```
|
|
110
107
|
|
|
111
108
|
### Get All Pages (Pagination Helper)
|
|
@@ -114,12 +111,12 @@ The `getAll` method automatically handles pagination and returns all results:
|
|
|
114
111
|
|
|
115
112
|
```typescript
|
|
116
113
|
// Get all users from 42Lyon (handles pagination automatically)
|
|
117
|
-
const lyonUsers = await client.getAll(
|
|
114
|
+
const lyonUsers = await client.getAll("campus/9/users");
|
|
118
115
|
console.log(`42Lyon campus has ${lyonUsers.length} users`);
|
|
119
116
|
|
|
120
117
|
// Get all projects with custom page size
|
|
121
|
-
const allProjects = await client.getAll(
|
|
122
|
-
|
|
118
|
+
const allProjects = await client.getAll("projects", {
|
|
119
|
+
perPage: 50, // 50 items per page (default: 100)
|
|
123
120
|
});
|
|
124
121
|
```
|
|
125
122
|
|
|
@@ -129,16 +126,16 @@ const allProjects = await client.getAll('projects', {
|
|
|
129
126
|
|
|
130
127
|
```typescript
|
|
131
128
|
// 1. Get OAuth authorization URL
|
|
132
|
-
const authUrl = client.getOAuthUrl(
|
|
129
|
+
const { url: authUrl } = client.getOAuthUrl();
|
|
133
130
|
// Redirect user to authUrl
|
|
134
131
|
|
|
135
132
|
// 2. Exchange authorization code for tokens
|
|
136
|
-
const tokens = await client.exchangeOAuthCode(code,
|
|
133
|
+
const tokens = await client.exchangeOAuthCode(code, "http://localhost:3000/callback");
|
|
137
134
|
// tokens contains: access_token, refresh_token, expires_in, etc.
|
|
138
135
|
|
|
139
136
|
// 3. Use tokens for authenticated requests (USER-SPECIFIC DATA)
|
|
140
|
-
const currentUser = await client.get(
|
|
141
|
-
|
|
137
|
+
const currentUser = await client.get("users/me", {
|
|
138
|
+
token: tokens,
|
|
142
139
|
});
|
|
143
140
|
console.log(`Welcome ${currentUser.displayname}!`);
|
|
144
141
|
```
|
|
@@ -147,12 +144,14 @@ console.log(`Welcome ${currentUser.displayname}!`);
|
|
|
147
144
|
|
|
148
145
|
```typescript
|
|
149
146
|
// Set redirect URI in constructor
|
|
150
|
-
const client = new
|
|
151
|
-
|
|
147
|
+
const client = new FortytwoIntraClient("client_id", "client_secret", {
|
|
148
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
152
149
|
});
|
|
153
150
|
|
|
154
151
|
// Or pass it to getOAuthUrl
|
|
155
|
-
const authUrl = client.getOAuthUrl(
|
|
152
|
+
const { url: authUrl } = client.getOAuthUrl({
|
|
153
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
154
|
+
});
|
|
156
155
|
```
|
|
157
156
|
|
|
158
157
|
## Utility Methods
|
|
@@ -161,13 +160,13 @@ const authUrl = client.getOAuthUrl('http://localhost:3000/callback');
|
|
|
161
160
|
|
|
162
161
|
```typescript
|
|
163
162
|
// Create a URL object for the API
|
|
164
|
-
const userUrl = client.URL(
|
|
163
|
+
const userUrl = client.URL("users/jdoe");
|
|
165
164
|
console.log(userUrl.toString()); // "https://api.intra.42.fr/v2/users/jdoe"
|
|
166
165
|
|
|
167
166
|
// Build URLs for specific endpoints
|
|
168
|
-
const campusUrl = client.URL(
|
|
169
|
-
const projectUrl = client.URL(
|
|
170
|
-
const coalitionUrl = client.URL(
|
|
167
|
+
const campusUrl = client.URL("campus/1/users");
|
|
168
|
+
const projectUrl = client.URL("projects/libft");
|
|
169
|
+
const coalitionUrl = client.URL("coalitions/1/users");
|
|
171
170
|
```
|
|
172
171
|
|
|
173
172
|
## Error Handling
|
|
@@ -176,16 +175,16 @@ The library includes automatic retry logic for rate limiting (429) and authentic
|
|
|
176
175
|
|
|
177
176
|
```typescript
|
|
178
177
|
try {
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
const user = await client.get("users/jdoe");
|
|
179
|
+
console.log(`Found user: ${user.displayname}`);
|
|
181
180
|
} catch (error) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
181
|
+
if (error.statusCode === 404) {
|
|
182
|
+
console.error("User not found");
|
|
183
|
+
} else if (error.statusCode === 403) {
|
|
184
|
+
console.error("Access forbidden - check your token permissions");
|
|
185
|
+
} else {
|
|
186
|
+
console.error("Request failed after retries:", error.message);
|
|
187
|
+
}
|
|
189
188
|
}
|
|
190
189
|
```
|
|
191
190
|
|
|
@@ -211,60 +210,60 @@ Disable logging by setting `logLine: false` in the configuration. Control error
|
|
|
211
210
|
This library is written in TypeScript and includes full type definitions:
|
|
212
211
|
|
|
213
212
|
```typescript
|
|
214
|
-
import { FortytwoIntraClient } from
|
|
213
|
+
import { FortytwoIntraClient } from "@ibertran/fortytwo-intra-client";
|
|
215
214
|
|
|
216
215
|
// Types are automatically inferred
|
|
217
|
-
const client = new FortytwoIntraClient(
|
|
216
|
+
const client = new FortytwoIntraClient("client_id", "client_secret");
|
|
218
217
|
```
|
|
219
218
|
|
|
220
219
|
### OAuth Flow Example
|
|
221
220
|
|
|
222
221
|
```typescript
|
|
223
|
-
import express from
|
|
224
|
-
import { FortytwoIntraClient } from
|
|
222
|
+
import express from "express";
|
|
223
|
+
import { FortytwoIntraClient } from "@ibertran/fortytwo-intra-client";
|
|
225
224
|
|
|
226
225
|
const app = express();
|
|
227
226
|
const client = new FortytwoIntraClient(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
process.env.INTRA_CLIENT_ID!,
|
|
228
|
+
process.env.INTRA_CLIENT_SECRET!,
|
|
229
|
+
{
|
|
230
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
231
|
+
scopes: ["public"],
|
|
232
|
+
},
|
|
234
233
|
);
|
|
235
234
|
|
|
236
235
|
// Redirect to 42 OAuth
|
|
237
|
-
app.get(
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
app.get("/login", (req, res) => {
|
|
237
|
+
const { url: authUrl } = client.getOAuthUrl();
|
|
238
|
+
res.redirect(authUrl);
|
|
240
239
|
});
|
|
241
240
|
|
|
242
241
|
// Handle OAuth callback
|
|
243
|
-
app.get(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
242
|
+
app.get("/callback", async (req, res) => {
|
|
243
|
+
try {
|
|
244
|
+
const { code } = req.query;
|
|
245
|
+
const token = await client.exchangeOAuthCode(code as string);
|
|
246
|
+
|
|
247
|
+
// Use tokens to make authenticated requests (accessing personal data)
|
|
248
|
+
const userData = await client.get("users/me", { token: token });
|
|
249
|
+
|
|
250
|
+
res.json({
|
|
251
|
+
user: {
|
|
252
|
+
login: userData.login,
|
|
253
|
+
displayname: userData.displayname,
|
|
254
|
+
email: userData.email,
|
|
255
|
+
level: userData.cursus_users[0]?.level || 0,
|
|
256
|
+
wallet: userData.wallet,
|
|
257
|
+
campus: userData.campus[0]?.name,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
res.status(500).json({ error: error.message });
|
|
262
|
+
}
|
|
264
263
|
});
|
|
265
264
|
|
|
266
265
|
app.listen(3000, () => {
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
console.log("Server running on http://localhost:3000");
|
|
267
|
+
console.log("Visit http://localhost:3000/login to start OAuth flow");
|
|
269
268
|
});
|
|
270
269
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { inputOptions } from "./types";
|
|
2
|
+
import { FortytwoIntraClientError, isFortytwoIntraClientError } from "./lib/errors";
|
|
3
|
+
import { z } from "zod";
|
|
2
4
|
export interface FortytwoIntraClientConf {
|
|
3
5
|
redirect_uri: string | null;
|
|
4
6
|
base_url: string;
|
|
@@ -11,7 +13,6 @@ export interface FortytwoIntraClientConf {
|
|
|
11
13
|
maxRetry: number;
|
|
12
14
|
logLine: boolean;
|
|
13
15
|
errLogBody: boolean;
|
|
14
|
-
throwOnError: boolean;
|
|
15
16
|
}
|
|
16
17
|
export declare class FortytwoIntraClient {
|
|
17
18
|
private client_id;
|
|
@@ -29,7 +30,6 @@ export declare class FortytwoIntraClient {
|
|
|
29
30
|
private maxRetry;
|
|
30
31
|
private logLine;
|
|
31
32
|
private errLogBody;
|
|
32
|
-
private throwOnError;
|
|
33
33
|
private access_token;
|
|
34
34
|
constructor(client_id: string, client_secret: string, conf: Partial<FortytwoIntraClientConf>);
|
|
35
35
|
private generateToken;
|
|
@@ -38,19 +38,38 @@ export declare class FortytwoIntraClient {
|
|
|
38
38
|
private logSuccess;
|
|
39
39
|
private logError;
|
|
40
40
|
get(endpoint: URL | string, options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
|
|
41
|
+
get<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "body" | "perPage" | "maxPage"> & {
|
|
42
|
+
schema: S;
|
|
43
|
+
}): Promise<z.infer<S>>;
|
|
41
44
|
post(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
|
|
45
|
+
post<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
|
|
46
|
+
schema: S;
|
|
47
|
+
}): Promise<z.infer<S>>;
|
|
42
48
|
put(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
|
|
49
|
+
put<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
|
|
50
|
+
schema: S;
|
|
51
|
+
}): Promise<z.infer<S>>;
|
|
43
52
|
patch(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
|
|
53
|
+
patch<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
|
|
54
|
+
schema: S;
|
|
55
|
+
}): Promise<z.infer<S>>;
|
|
44
56
|
delete(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
|
|
57
|
+
delete<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
|
|
58
|
+
schema: S;
|
|
59
|
+
}): Promise<z.infer<S>>;
|
|
45
60
|
getAll(endpoint: URL | string, options?: Omit<inputOptions, "body">): Promise<any>;
|
|
61
|
+
getAll<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "body"> & {
|
|
62
|
+
schema: S;
|
|
63
|
+
}): Promise<z.infer<S>>;
|
|
46
64
|
URL(endpoint: string): URL;
|
|
47
65
|
getOAuthUrl(options?: {
|
|
48
66
|
redirect_uri?: string;
|
|
49
67
|
state?: string;
|
|
50
68
|
}): {
|
|
51
69
|
url: string;
|
|
52
|
-
state: string;
|
|
70
|
+
state: string | null;
|
|
53
71
|
};
|
|
54
72
|
exchangeOAuthCode(code: string, redirect_uri?: string): Promise<any>;
|
|
55
73
|
tokenInfos(options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
|
|
56
74
|
}
|
|
75
|
+
export { FortytwoIntraClientError, isFortytwoIntraClientError };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.FortytwoIntraClient = void 0;
|
|
7
|
-
const axios_1 =
|
|
39
|
+
exports.isFortytwoIntraClientError = exports.FortytwoIntraClientError = exports.FortytwoIntraClient = void 0;
|
|
40
|
+
const axios_1 = __importStar(require("axios"));
|
|
8
41
|
const axios_rate_limit_1 = __importDefault(require("axios-rate-limit"));
|
|
9
42
|
const pagination_1 = require("./lib/pagination");
|
|
10
43
|
const logs_1 = require("./lib/logs");
|
|
11
|
-
const
|
|
44
|
+
const errors_1 = require("./lib/errors");
|
|
45
|
+
Object.defineProperty(exports, "FortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.FortytwoIntraClientError; } });
|
|
46
|
+
Object.defineProperty(exports, "isFortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.isFortytwoIntraClientError; } });
|
|
12
47
|
const defaultConf = {
|
|
13
48
|
redirect_uri: null,
|
|
14
49
|
base_url: "https://api.intra.42.fr/v2/",
|
|
@@ -21,7 +56,6 @@ const defaultConf = {
|
|
|
21
56
|
maxRetry: 5,
|
|
22
57
|
logLine: true,
|
|
23
58
|
errLogBody: true,
|
|
24
|
-
throwOnError: true,
|
|
25
59
|
};
|
|
26
60
|
class FortytwoIntraClient {
|
|
27
61
|
client_id;
|
|
@@ -39,7 +73,6 @@ class FortytwoIntraClient {
|
|
|
39
73
|
maxRetry;
|
|
40
74
|
logLine;
|
|
41
75
|
errLogBody;
|
|
42
|
-
throwOnError;
|
|
43
76
|
access_token;
|
|
44
77
|
constructor(client_id, client_secret, conf) {
|
|
45
78
|
this.client_id = client_id;
|
|
@@ -63,7 +96,6 @@ class FortytwoIntraClient {
|
|
|
63
96
|
this.maxRetry = config.maxRetry;
|
|
64
97
|
this.logLine = config.logLine;
|
|
65
98
|
this.errLogBody = config.errLogBody;
|
|
66
|
-
this.throwOnError = config.throwOnError;
|
|
67
99
|
this.access_token = null;
|
|
68
100
|
}
|
|
69
101
|
async generateToken() {
|
|
@@ -112,7 +144,7 @@ class FortytwoIntraClient {
|
|
|
112
144
|
return res;
|
|
113
145
|
}
|
|
114
146
|
catch (err) {
|
|
115
|
-
if (
|
|
147
|
+
if ((0, axios_1.isAxiosError)(err)) {
|
|
116
148
|
this.logError(err, options);
|
|
117
149
|
const { attempt, maxRetry } = options;
|
|
118
150
|
const status = err.response?.status;
|
|
@@ -124,8 +156,7 @@ class FortytwoIntraClient {
|
|
|
124
156
|
return this.reqHandler(url, options);
|
|
125
157
|
}
|
|
126
158
|
else {
|
|
127
|
-
|
|
128
|
-
throw err;
|
|
159
|
+
throw new errors_1.FortytwoIntraClientError(err);
|
|
129
160
|
}
|
|
130
161
|
}
|
|
131
162
|
else {
|
|
@@ -152,7 +183,6 @@ class FortytwoIntraClient {
|
|
|
152
183
|
console.log(line, logBody ? body : "");
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
|
-
// Public methods
|
|
156
186
|
async get(endpoint, options = {}) {
|
|
157
187
|
if (endpoint instanceof URL === false) {
|
|
158
188
|
endpoint = new URL(endpoint, this.base_url);
|
|
@@ -165,7 +195,10 @@ class FortytwoIntraClient {
|
|
|
165
195
|
errLogBody: this.errLogBody,
|
|
166
196
|
...options,
|
|
167
197
|
});
|
|
168
|
-
|
|
198
|
+
if (options.schema) {
|
|
199
|
+
return options.schema.parse(res.data);
|
|
200
|
+
}
|
|
201
|
+
return res.data;
|
|
169
202
|
}
|
|
170
203
|
async post(endpoint, options = {}) {
|
|
171
204
|
if (endpoint instanceof URL === false) {
|
|
@@ -179,7 +212,10 @@ class FortytwoIntraClient {
|
|
|
179
212
|
errLogBody: this.errLogBody,
|
|
180
213
|
...options,
|
|
181
214
|
});
|
|
182
|
-
|
|
215
|
+
if (options.schema) {
|
|
216
|
+
return options.schema.parse(res.data);
|
|
217
|
+
}
|
|
218
|
+
return res.data;
|
|
183
219
|
}
|
|
184
220
|
async put(endpoint, options = {}) {
|
|
185
221
|
if (endpoint instanceof URL === false) {
|
|
@@ -193,7 +229,10 @@ class FortytwoIntraClient {
|
|
|
193
229
|
errLogBody: this.errLogBody,
|
|
194
230
|
...options,
|
|
195
231
|
});
|
|
196
|
-
|
|
232
|
+
if (options.schema) {
|
|
233
|
+
return options.schema.parse(res.data);
|
|
234
|
+
}
|
|
235
|
+
return res.data;
|
|
197
236
|
}
|
|
198
237
|
async patch(endpoint, options = {}) {
|
|
199
238
|
if (endpoint instanceof URL === false) {
|
|
@@ -207,7 +246,10 @@ class FortytwoIntraClient {
|
|
|
207
246
|
errLogBody: this.errLogBody,
|
|
208
247
|
...options,
|
|
209
248
|
});
|
|
210
|
-
|
|
249
|
+
if (options.schema) {
|
|
250
|
+
return options.schema.parse(res.data);
|
|
251
|
+
}
|
|
252
|
+
return res.data;
|
|
211
253
|
}
|
|
212
254
|
async delete(endpoint, options = {}) {
|
|
213
255
|
if (endpoint instanceof URL === false) {
|
|
@@ -221,7 +263,10 @@ class FortytwoIntraClient {
|
|
|
221
263
|
errLogBody: this.errLogBody,
|
|
222
264
|
...options,
|
|
223
265
|
});
|
|
224
|
-
|
|
266
|
+
if (options.schema) {
|
|
267
|
+
return options.schema.parse(res.data);
|
|
268
|
+
}
|
|
269
|
+
return res.data;
|
|
225
270
|
}
|
|
226
271
|
async getAll(endpoint, options = {}) {
|
|
227
272
|
if (endpoint instanceof URL === false) {
|
|
@@ -229,7 +274,7 @@ class FortytwoIntraClient {
|
|
|
229
274
|
}
|
|
230
275
|
const perPage = options.perPage || 100;
|
|
231
276
|
let url = new URL(endpoint);
|
|
232
|
-
const
|
|
277
|
+
const firstPage = await this.reqHandler(url, {
|
|
233
278
|
method: "GET",
|
|
234
279
|
attempt: 0,
|
|
235
280
|
currpage: 1,
|
|
@@ -244,50 +289,62 @@ class FortytwoIntraClient {
|
|
|
244
289
|
per_page: perPage,
|
|
245
290
|
}
|
|
246
291
|
});
|
|
292
|
+
if (!Array.isArray(firstPage.data)) {
|
|
293
|
+
if (options.schema) {
|
|
294
|
+
return options.schema.parse(firstPage.data);
|
|
295
|
+
}
|
|
296
|
+
return firstPage.data;
|
|
297
|
+
}
|
|
247
298
|
let lastPage;
|
|
248
299
|
try {
|
|
249
|
-
lastPage = Math.min((0, pagination_1.getLastPage)(
|
|
300
|
+
lastPage = Math.min((0, pagination_1.getLastPage)(firstPage.headers["link"]), options.maxPages || Infinity);
|
|
250
301
|
}
|
|
251
302
|
catch (err) {
|
|
252
|
-
|
|
303
|
+
if (options.schema) {
|
|
304
|
+
return options.schema.parse(firstPage.data);
|
|
305
|
+
}
|
|
306
|
+
return firstPage.data;
|
|
253
307
|
}
|
|
254
|
-
const promises =
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
...options,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
308
|
+
const promises = Array.from({ length: lastPage - 1 }, (_, i) => i + 2)
|
|
309
|
+
.map(pageNumber => this.reqHandler(url, {
|
|
310
|
+
method: "GET",
|
|
311
|
+
attempt: 0,
|
|
312
|
+
currpage: pageNumber,
|
|
313
|
+
lastPage: lastPage,
|
|
314
|
+
maxRetry: this.maxRetry,
|
|
315
|
+
logLine: this.logLine,
|
|
316
|
+
errLogBody: this.errLogBody,
|
|
317
|
+
...options,
|
|
318
|
+
query: {
|
|
319
|
+
...options.query,
|
|
320
|
+
page: pageNumber,
|
|
321
|
+
per_page: perPage,
|
|
322
|
+
}
|
|
323
|
+
}));
|
|
324
|
+
const otherPages = await Promise.all(promises);
|
|
325
|
+
const allData = [...firstPage.data, ...otherPages.flatMap((res) => res.data)];
|
|
326
|
+
if (options.schema) {
|
|
327
|
+
return options.schema.parse(allData);
|
|
272
328
|
}
|
|
273
|
-
return
|
|
274
|
-
return initialRes?.data.concat(...values.map((value) => value.data));
|
|
275
|
-
});
|
|
329
|
+
return allData;
|
|
276
330
|
}
|
|
277
331
|
URL(endpoint) {
|
|
278
332
|
return new URL(endpoint, this.base_url);
|
|
279
333
|
}
|
|
280
334
|
getOAuthUrl(options = {}) {
|
|
281
335
|
const redirectUri = options.redirect_uri || this.redirect_uri;
|
|
282
|
-
if (!redirectUri)
|
|
336
|
+
if (!redirectUri) {
|
|
283
337
|
throw new Error(`Missing redirect_uri parameter`);
|
|
284
|
-
|
|
338
|
+
}
|
|
285
339
|
const url = new URL(this.oauth_url);
|
|
286
340
|
url.searchParams.set("client_id", this.client_id);
|
|
287
341
|
url.searchParams.set("redirect_uri", redirectUri);
|
|
288
342
|
url.searchParams.set("response_type", "code");
|
|
289
343
|
url.searchParams.set("scope", this.scopes.join(" "));
|
|
290
|
-
|
|
344
|
+
const state = options.state ?? null;
|
|
345
|
+
if (state !== null) {
|
|
346
|
+
url.searchParams.set("state", state);
|
|
347
|
+
}
|
|
291
348
|
return { url: url.toString(), state };
|
|
292
349
|
}
|
|
293
350
|
async exchangeOAuthCode(code, redirect_uri) {
|
|
@@ -298,7 +355,7 @@ class FortytwoIntraClient {
|
|
|
298
355
|
redirect_uri: redirect_uri ? redirect_uri : this.redirect_uri,
|
|
299
356
|
code: code,
|
|
300
357
|
});
|
|
301
|
-
return res
|
|
358
|
+
return res.data;
|
|
302
359
|
}
|
|
303
360
|
async tokenInfos(options = {}) {
|
|
304
361
|
return this.get(this.token_info_url, options);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AxiosError } from "axios";
|
|
2
|
+
export declare class FortytwoIntraClientError extends Error {
|
|
3
|
+
statusCode: number;
|
|
4
|
+
statusText: string;
|
|
5
|
+
data: unknown;
|
|
6
|
+
constructor(error: AxiosError);
|
|
7
|
+
}
|
|
8
|
+
export declare function isFortytwoIntraClientError(error: unknown): error is FortytwoIntraClientError;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FortytwoIntraClientError = void 0;
|
|
4
|
+
exports.isFortytwoIntraClientError = isFortytwoIntraClientError;
|
|
5
|
+
class FortytwoIntraClientError extends Error {
|
|
6
|
+
statusCode;
|
|
7
|
+
statusText;
|
|
8
|
+
data;
|
|
9
|
+
constructor(error) {
|
|
10
|
+
const method = error.config?.method?.toUpperCase() ?? null;
|
|
11
|
+
const route = error.config?.url ?? null;
|
|
12
|
+
const statusCode = error.response?.status ?? NaN;
|
|
13
|
+
const statusText = error.response?.statusText ?? "Unknown";
|
|
14
|
+
const message = method && route
|
|
15
|
+
? `${method} ${route} - HTTP ${statusCode} ${statusText}`
|
|
16
|
+
: error.message;
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "FortytwoIntraClientError";
|
|
19
|
+
this.statusCode = statusCode;
|
|
20
|
+
this.statusText = statusText;
|
|
21
|
+
this.data = error.response?.data;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.FortytwoIntraClientError = FortytwoIntraClientError;
|
|
25
|
+
function isFortytwoIntraClientError(error) {
|
|
26
|
+
return error instanceof FortytwoIntraClientError;
|
|
27
|
+
}
|
package/dist/lib/logs.js
CHANGED
|
@@ -5,7 +5,6 @@ exports.getErrorLogLine = getErrorLogLine;
|
|
|
5
5
|
const util_1 = require("util");
|
|
6
6
|
const green = "\x1b[42m";
|
|
7
7
|
const red = "\x1b[41m";
|
|
8
|
-
const yellow = "\x1b[43m";
|
|
9
8
|
const reset = "\x1b[0m";
|
|
10
9
|
function formatQueryParams(params) {
|
|
11
10
|
const { per_page, page, ...filteredParams } = params;
|
package/package.json
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
"name": "@ibertran/fortytwo-intra-client",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Ian BERTRAND"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"format": "prettier --write .",
|
|
12
|
+
"prettier": "prettier --check ."
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"axios": "^1.15.2",
|
|
16
|
+
"axios-rate-limit": "^1.9.0",
|
|
17
|
+
"zod": "^4.3.6"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^25.6.0",
|
|
21
|
+
"dotenv": "^17.4.2",
|
|
22
|
+
"express": "^5.2.1",
|
|
23
|
+
"prettier": "^3.8.3",
|
|
24
|
+
"ts-node": "^10.9.2",
|
|
25
|
+
"typescript": "^6.0.3"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md"
|
|
30
|
+
]
|
|
26
31
|
}
|
package/dist/HttpError.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
declare class HttpError extends Error {
|
|
2
|
-
status: number | null;
|
|
3
|
-
url: string | null;
|
|
4
|
-
details: any;
|
|
5
|
-
constructor(err: any);
|
|
6
|
-
}
|
|
7
|
-
export declare function simplifySuperagentError(err: any): HttpError;
|
|
8
|
-
export declare function isSuperAgentError(err: any): boolean;
|
|
9
|
-
export {};
|
package/dist/HttpError.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.simplifySuperagentError = simplifySuperagentError;
|
|
4
|
-
exports.isSuperAgentError = isSuperAgentError;
|
|
5
|
-
class HttpError extends Error {
|
|
6
|
-
status;
|
|
7
|
-
url;
|
|
8
|
-
details;
|
|
9
|
-
constructor(err) {
|
|
10
|
-
super(err.message || "Unknown API error");
|
|
11
|
-
this.name = "HttpError";
|
|
12
|
-
this.status = err.status || null;
|
|
13
|
-
this.url = err.response?.request?.url || null;
|
|
14
|
-
this.details = err.response?.body || null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function simplifySuperagentError(err) {
|
|
18
|
-
if (!err) {
|
|
19
|
-
return new HttpError({ message: "Unknown error" });
|
|
20
|
-
}
|
|
21
|
-
return new HttpError(err);
|
|
22
|
-
}
|
|
23
|
-
function isSuperAgentError(err) {
|
|
24
|
-
return (err && typeof err === "object" && ("status" in err || "response" in err));
|
|
25
|
-
}
|
package/dist/utils.d.ts
DELETED
package/dist/utils.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.initRequest = initRequest;
|
|
37
|
-
exports.getLastPage = getLastPage;
|
|
38
|
-
const superagent = __importStar(require("superagent"));
|
|
39
|
-
function initRequest(method, url) {
|
|
40
|
-
const endpoint = url.toString();
|
|
41
|
-
switch (method) {
|
|
42
|
-
case "GET":
|
|
43
|
-
return superagent.get(endpoint);
|
|
44
|
-
case "POST":
|
|
45
|
-
return superagent.post(endpoint);
|
|
46
|
-
case "PATCH":
|
|
47
|
-
return superagent.patch(endpoint);
|
|
48
|
-
case "DELETE":
|
|
49
|
-
return superagent.delete(endpoint);
|
|
50
|
-
default:
|
|
51
|
-
throw Error("Invalid HTTP method");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function getLastPage(header) {
|
|
55
|
-
if (!header) {
|
|
56
|
-
throw Error("No pagination link header");
|
|
57
|
-
}
|
|
58
|
-
const paginationlinks = header
|
|
59
|
-
.split(",")
|
|
60
|
-
.reduce((links, part) => {
|
|
61
|
-
const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
|
|
62
|
-
if (match) {
|
|
63
|
-
const url = match[1];
|
|
64
|
-
const rel = match[2];
|
|
65
|
-
links[rel] = url;
|
|
66
|
-
}
|
|
67
|
-
return links;
|
|
68
|
-
}, {});
|
|
69
|
-
if (!paginationlinks.last) {
|
|
70
|
-
throw Error("Missing last page link");
|
|
71
|
-
}
|
|
72
|
-
const lastPageUrl = new URL(paginationlinks.last);
|
|
73
|
-
const rawLastPage = lastPageUrl.searchParams.get("page");
|
|
74
|
-
const lastPage = rawLastPage ? parseInt(rawLastPage, 10) : undefined;
|
|
75
|
-
if (!lastPage || isNaN(lastPage)) {
|
|
76
|
-
throw Error("Invalid last page link");
|
|
77
|
-
}
|
|
78
|
-
return lastPage;
|
|
79
|
-
}
|