@ibertran/fortytwo-intra-client 3.2.0 → 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 -96
- package/dist/index.d.ts +20 -3
- package/dist/index.js +94 -42
- package/dist/lib/errors.d.ts +4 -5
- package/dist/lib/errors.js +0 -4
- 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,37 +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
|
-
|
|
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)
|
|
47
46
|
}
|
|
48
47
|
```
|
|
49
48
|
|
|
50
49
|
### Example with Custom Configuration
|
|
51
50
|
|
|
52
51
|
```typescript
|
|
53
|
-
const client = new FortytwoIntraClient(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
60
59
|
});
|
|
61
60
|
```
|
|
62
61
|
|
|
@@ -66,11 +65,11 @@ const client = new FortytwoIntraClient('your_client_id', 'your_client_secret', {
|
|
|
66
65
|
|
|
67
66
|
```typescript
|
|
68
67
|
// Get all campuses
|
|
69
|
-
const campuses = await client.get(
|
|
68
|
+
const campuses = await client.get("campus");
|
|
70
69
|
console.log(`Found ${campuses.length} campuses`);
|
|
71
70
|
|
|
72
71
|
// Get a specific campus
|
|
73
|
-
const lyonCampus = await client.get(
|
|
72
|
+
const lyonCampus = await client.get("campus/9");
|
|
74
73
|
console.log(`${lyonCampus.name}: ${lyonCampus.users_count} users`);
|
|
75
74
|
```
|
|
76
75
|
|
|
@@ -78,11 +77,11 @@ console.log(`${lyonCampus.name}: ${lyonCampus.users_count} users`);
|
|
|
78
77
|
|
|
79
78
|
```typescript
|
|
80
79
|
// Create a team for a project
|
|
81
|
-
const team = await client.post(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const team = await client.post("teams", {
|
|
81
|
+
body: {
|
|
82
|
+
name: "awesome-team",
|
|
83
|
+
project_id: 42,
|
|
84
|
+
},
|
|
86
85
|
});
|
|
87
86
|
```
|
|
88
87
|
|
|
@@ -90,12 +89,12 @@ const team = await client.post('teams', {
|
|
|
90
89
|
|
|
91
90
|
```typescript
|
|
92
91
|
// Update a team name
|
|
93
|
-
const updatedTeam = await client.patch(
|
|
94
|
-
|
|
92
|
+
const updatedTeam = await client.patch("teams/123", {
|
|
93
|
+
body: {
|
|
95
94
|
team: {
|
|
96
|
-
name: "updated-team-name"
|
|
97
|
-
}
|
|
98
|
-
|
|
95
|
+
name: "updated-team-name",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
99
98
|
});
|
|
100
99
|
```
|
|
101
100
|
|
|
@@ -103,7 +102,7 @@ const updatedTeam = await client.patch('teams/123', {
|
|
|
103
102
|
|
|
104
103
|
```typescript
|
|
105
104
|
// Delete a team
|
|
106
|
-
await client.delete(
|
|
105
|
+
await client.delete("teams/123");
|
|
107
106
|
```
|
|
108
107
|
|
|
109
108
|
### Get All Pages (Pagination Helper)
|
|
@@ -112,12 +111,12 @@ The `getAll` method automatically handles pagination and returns all results:
|
|
|
112
111
|
|
|
113
112
|
```typescript
|
|
114
113
|
// Get all users from 42Lyon (handles pagination automatically)
|
|
115
|
-
const lyonUsers = await client.getAll(
|
|
114
|
+
const lyonUsers = await client.getAll("campus/9/users");
|
|
116
115
|
console.log(`42Lyon campus has ${lyonUsers.length} users`);
|
|
117
116
|
|
|
118
117
|
// Get all projects with custom page size
|
|
119
|
-
const allProjects = await client.getAll(
|
|
120
|
-
|
|
118
|
+
const allProjects = await client.getAll("projects", {
|
|
119
|
+
perPage: 50, // 50 items per page (default: 100)
|
|
121
120
|
});
|
|
122
121
|
```
|
|
123
122
|
|
|
@@ -127,16 +126,16 @@ const allProjects = await client.getAll('projects', {
|
|
|
127
126
|
|
|
128
127
|
```typescript
|
|
129
128
|
// 1. Get OAuth authorization URL
|
|
130
|
-
const authUrl = client.getOAuthUrl(
|
|
129
|
+
const { url: authUrl } = client.getOAuthUrl();
|
|
131
130
|
// Redirect user to authUrl
|
|
132
131
|
|
|
133
132
|
// 2. Exchange authorization code for tokens
|
|
134
|
-
const tokens = await client.exchangeOAuthCode(code,
|
|
133
|
+
const tokens = await client.exchangeOAuthCode(code, "http://localhost:3000/callback");
|
|
135
134
|
// tokens contains: access_token, refresh_token, expires_in, etc.
|
|
136
135
|
|
|
137
136
|
// 3. Use tokens for authenticated requests (USER-SPECIFIC DATA)
|
|
138
|
-
const currentUser = await client.get(
|
|
139
|
-
|
|
137
|
+
const currentUser = await client.get("users/me", {
|
|
138
|
+
token: tokens,
|
|
140
139
|
});
|
|
141
140
|
console.log(`Welcome ${currentUser.displayname}!`);
|
|
142
141
|
```
|
|
@@ -145,12 +144,14 @@ console.log(`Welcome ${currentUser.displayname}!`);
|
|
|
145
144
|
|
|
146
145
|
```typescript
|
|
147
146
|
// Set redirect URI in constructor
|
|
148
|
-
const client = new
|
|
149
|
-
|
|
147
|
+
const client = new FortytwoIntraClient("client_id", "client_secret", {
|
|
148
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
150
149
|
});
|
|
151
150
|
|
|
152
151
|
// Or pass it to getOAuthUrl
|
|
153
|
-
const authUrl = client.getOAuthUrl(
|
|
152
|
+
const { url: authUrl } = client.getOAuthUrl({
|
|
153
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
154
|
+
});
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
## Utility Methods
|
|
@@ -159,13 +160,13 @@ const authUrl = client.getOAuthUrl('http://localhost:3000/callback');
|
|
|
159
160
|
|
|
160
161
|
```typescript
|
|
161
162
|
// Create a URL object for the API
|
|
162
|
-
const userUrl = client.URL(
|
|
163
|
+
const userUrl = client.URL("users/jdoe");
|
|
163
164
|
console.log(userUrl.toString()); // "https://api.intra.42.fr/v2/users/jdoe"
|
|
164
165
|
|
|
165
166
|
// Build URLs for specific endpoints
|
|
166
|
-
const campusUrl = client.URL(
|
|
167
|
-
const projectUrl = client.URL(
|
|
168
|
-
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");
|
|
169
170
|
```
|
|
170
171
|
|
|
171
172
|
## Error Handling
|
|
@@ -174,16 +175,16 @@ The library includes automatic retry logic for rate limiting (429) and authentic
|
|
|
174
175
|
|
|
175
176
|
```typescript
|
|
176
177
|
try {
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
const user = await client.get("users/jdoe");
|
|
179
|
+
console.log(`Found user: ${user.displayname}`);
|
|
179
180
|
} catch (error) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
+
}
|
|
187
188
|
}
|
|
188
189
|
```
|
|
189
190
|
|
|
@@ -209,60 +210,60 @@ Disable logging by setting `logLine: false` in the configuration. Control error
|
|
|
209
210
|
This library is written in TypeScript and includes full type definitions:
|
|
210
211
|
|
|
211
212
|
```typescript
|
|
212
|
-
import { FortytwoIntraClient } from
|
|
213
|
+
import { FortytwoIntraClient } from "@ibertran/fortytwo-intra-client";
|
|
213
214
|
|
|
214
215
|
// Types are automatically inferred
|
|
215
|
-
const client = new FortytwoIntraClient(
|
|
216
|
+
const client = new FortytwoIntraClient("client_id", "client_secret");
|
|
216
217
|
```
|
|
217
218
|
|
|
218
219
|
### OAuth Flow Example
|
|
219
220
|
|
|
220
221
|
```typescript
|
|
221
|
-
import express from
|
|
222
|
-
import { FortytwoIntraClient } from
|
|
222
|
+
import express from "express";
|
|
223
|
+
import { FortytwoIntraClient } from "@ibertran/fortytwo-intra-client";
|
|
223
224
|
|
|
224
225
|
const app = express();
|
|
225
226
|
const client = new FortytwoIntraClient(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
227
|
+
process.env.INTRA_CLIENT_ID!,
|
|
228
|
+
process.env.INTRA_CLIENT_SECRET!,
|
|
229
|
+
{
|
|
230
|
+
redirect_uri: "http://localhost:3000/callback",
|
|
231
|
+
scopes: ["public"],
|
|
232
|
+
},
|
|
232
233
|
);
|
|
233
234
|
|
|
234
235
|
// Redirect to 42 OAuth
|
|
235
|
-
app.get(
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
app.get("/login", (req, res) => {
|
|
237
|
+
const { url: authUrl } = client.getOAuthUrl();
|
|
238
|
+
res.redirect(authUrl);
|
|
238
239
|
});
|
|
239
240
|
|
|
240
241
|
// Handle OAuth callback
|
|
241
|
-
app.get(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
+
}
|
|
262
263
|
});
|
|
263
264
|
|
|
264
265
|
app.listen(3000, () => {
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
console.log("Server running on http://localhost:3000");
|
|
267
|
+
console.log("Visit http://localhost:3000/login to start OAuth flow");
|
|
267
268
|
});
|
|
268
269
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { inputOptions } from "./types";
|
|
2
2
|
import { FortytwoIntraClientError, isFortytwoIntraClientError } from "./lib/errors";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
export interface FortytwoIntraClientConf {
|
|
4
5
|
redirect_uri: string | null;
|
|
5
6
|
base_url: string;
|
|
@@ -12,7 +13,6 @@ export interface FortytwoIntraClientConf {
|
|
|
12
13
|
maxRetry: number;
|
|
13
14
|
logLine: boolean;
|
|
14
15
|
errLogBody: boolean;
|
|
15
|
-
throwOnError: boolean;
|
|
16
16
|
}
|
|
17
17
|
export declare class FortytwoIntraClient {
|
|
18
18
|
private client_id;
|
|
@@ -30,7 +30,6 @@ export declare class FortytwoIntraClient {
|
|
|
30
30
|
private maxRetry;
|
|
31
31
|
private logLine;
|
|
32
32
|
private errLogBody;
|
|
33
|
-
private throwOnError;
|
|
34
33
|
private access_token;
|
|
35
34
|
constructor(client_id: string, client_secret: string, conf: Partial<FortytwoIntraClientConf>);
|
|
36
35
|
private generateToken;
|
|
@@ -39,18 +38,36 @@ export declare class FortytwoIntraClient {
|
|
|
39
38
|
private logSuccess;
|
|
40
39
|
private logError;
|
|
41
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>>;
|
|
42
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>>;
|
|
43
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>>;
|
|
44
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>>;
|
|
45
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>>;
|
|
46
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>>;
|
|
47
64
|
URL(endpoint: string): URL;
|
|
48
65
|
getOAuthUrl(options?: {
|
|
49
66
|
redirect_uri?: string;
|
|
50
67
|
state?: string;
|
|
51
68
|
}): {
|
|
52
69
|
url: string;
|
|
53
|
-
state: string;
|
|
70
|
+
state: string | null;
|
|
54
71
|
};
|
|
55
72
|
exchangeOAuthCode(code: string, redirect_uri?: string): Promise<any>;
|
|
56
73
|
tokenInfos(options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
|
package/dist/index.js
CHANGED
|
@@ -1,17 +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
39
|
exports.isFortytwoIntraClientError = exports.FortytwoIntraClientError = exports.FortytwoIntraClient = void 0;
|
|
7
|
-
const axios_1 =
|
|
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
44
|
const errors_1 = require("./lib/errors");
|
|
12
45
|
Object.defineProperty(exports, "FortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.FortytwoIntraClientError; } });
|
|
13
46
|
Object.defineProperty(exports, "isFortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.isFortytwoIntraClientError; } });
|
|
14
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
15
47
|
const defaultConf = {
|
|
16
48
|
redirect_uri: null,
|
|
17
49
|
base_url: "https://api.intra.42.fr/v2/",
|
|
@@ -24,7 +56,6 @@ const defaultConf = {
|
|
|
24
56
|
maxRetry: 5,
|
|
25
57
|
logLine: true,
|
|
26
58
|
errLogBody: true,
|
|
27
|
-
throwOnError: true,
|
|
28
59
|
};
|
|
29
60
|
class FortytwoIntraClient {
|
|
30
61
|
client_id;
|
|
@@ -42,7 +73,6 @@ class FortytwoIntraClient {
|
|
|
42
73
|
maxRetry;
|
|
43
74
|
logLine;
|
|
44
75
|
errLogBody;
|
|
45
|
-
throwOnError;
|
|
46
76
|
access_token;
|
|
47
77
|
constructor(client_id, client_secret, conf) {
|
|
48
78
|
this.client_id = client_id;
|
|
@@ -66,7 +96,6 @@ class FortytwoIntraClient {
|
|
|
66
96
|
this.maxRetry = config.maxRetry;
|
|
67
97
|
this.logLine = config.logLine;
|
|
68
98
|
this.errLogBody = config.errLogBody;
|
|
69
|
-
this.throwOnError = config.throwOnError;
|
|
70
99
|
this.access_token = null;
|
|
71
100
|
}
|
|
72
101
|
async generateToken() {
|
|
@@ -115,7 +144,7 @@ class FortytwoIntraClient {
|
|
|
115
144
|
return res;
|
|
116
145
|
}
|
|
117
146
|
catch (err) {
|
|
118
|
-
if (
|
|
147
|
+
if ((0, axios_1.isAxiosError)(err)) {
|
|
119
148
|
this.logError(err, options);
|
|
120
149
|
const { attempt, maxRetry } = options;
|
|
121
150
|
const status = err.response?.status;
|
|
@@ -127,11 +156,7 @@ class FortytwoIntraClient {
|
|
|
127
156
|
return this.reqHandler(url, options);
|
|
128
157
|
}
|
|
129
158
|
else {
|
|
130
|
-
|
|
131
|
-
if (this.throwOnError) {
|
|
132
|
-
throw clientError;
|
|
133
|
-
}
|
|
134
|
-
return clientError;
|
|
159
|
+
throw new errors_1.FortytwoIntraClientError(err);
|
|
135
160
|
}
|
|
136
161
|
}
|
|
137
162
|
else {
|
|
@@ -158,7 +183,6 @@ class FortytwoIntraClient {
|
|
|
158
183
|
console.log(line, logBody ? body : "");
|
|
159
184
|
}
|
|
160
185
|
}
|
|
161
|
-
// Public methods
|
|
162
186
|
async get(endpoint, options = {}) {
|
|
163
187
|
if (endpoint instanceof URL === false) {
|
|
164
188
|
endpoint = new URL(endpoint, this.base_url);
|
|
@@ -171,7 +195,10 @@ class FortytwoIntraClient {
|
|
|
171
195
|
errLogBody: this.errLogBody,
|
|
172
196
|
...options,
|
|
173
197
|
});
|
|
174
|
-
|
|
198
|
+
if (options.schema) {
|
|
199
|
+
return options.schema.parse(res.data);
|
|
200
|
+
}
|
|
201
|
+
return res.data;
|
|
175
202
|
}
|
|
176
203
|
async post(endpoint, options = {}) {
|
|
177
204
|
if (endpoint instanceof URL === false) {
|
|
@@ -185,7 +212,10 @@ class FortytwoIntraClient {
|
|
|
185
212
|
errLogBody: this.errLogBody,
|
|
186
213
|
...options,
|
|
187
214
|
});
|
|
188
|
-
|
|
215
|
+
if (options.schema) {
|
|
216
|
+
return options.schema.parse(res.data);
|
|
217
|
+
}
|
|
218
|
+
return res.data;
|
|
189
219
|
}
|
|
190
220
|
async put(endpoint, options = {}) {
|
|
191
221
|
if (endpoint instanceof URL === false) {
|
|
@@ -199,7 +229,10 @@ class FortytwoIntraClient {
|
|
|
199
229
|
errLogBody: this.errLogBody,
|
|
200
230
|
...options,
|
|
201
231
|
});
|
|
202
|
-
|
|
232
|
+
if (options.schema) {
|
|
233
|
+
return options.schema.parse(res.data);
|
|
234
|
+
}
|
|
235
|
+
return res.data;
|
|
203
236
|
}
|
|
204
237
|
async patch(endpoint, options = {}) {
|
|
205
238
|
if (endpoint instanceof URL === false) {
|
|
@@ -213,7 +246,10 @@ class FortytwoIntraClient {
|
|
|
213
246
|
errLogBody: this.errLogBody,
|
|
214
247
|
...options,
|
|
215
248
|
});
|
|
216
|
-
|
|
249
|
+
if (options.schema) {
|
|
250
|
+
return options.schema.parse(res.data);
|
|
251
|
+
}
|
|
252
|
+
return res.data;
|
|
217
253
|
}
|
|
218
254
|
async delete(endpoint, options = {}) {
|
|
219
255
|
if (endpoint instanceof URL === false) {
|
|
@@ -227,7 +263,10 @@ class FortytwoIntraClient {
|
|
|
227
263
|
errLogBody: this.errLogBody,
|
|
228
264
|
...options,
|
|
229
265
|
});
|
|
230
|
-
|
|
266
|
+
if (options.schema) {
|
|
267
|
+
return options.schema.parse(res.data);
|
|
268
|
+
}
|
|
269
|
+
return res.data;
|
|
231
270
|
}
|
|
232
271
|
async getAll(endpoint, options = {}) {
|
|
233
272
|
if (endpoint instanceof URL === false) {
|
|
@@ -250,49 +289,62 @@ class FortytwoIntraClient {
|
|
|
250
289
|
per_page: perPage,
|
|
251
290
|
}
|
|
252
291
|
});
|
|
292
|
+
if (!Array.isArray(firstPage.data)) {
|
|
293
|
+
if (options.schema) {
|
|
294
|
+
return options.schema.parse(firstPage.data);
|
|
295
|
+
}
|
|
296
|
+
return firstPage.data;
|
|
297
|
+
}
|
|
253
298
|
let lastPage;
|
|
254
299
|
try {
|
|
255
300
|
lastPage = Math.min((0, pagination_1.getLastPage)(firstPage.headers["link"]), options.maxPages || Infinity);
|
|
256
301
|
}
|
|
257
302
|
catch (err) {
|
|
258
|
-
|
|
303
|
+
if (options.schema) {
|
|
304
|
+
return options.schema.parse(firstPage.data);
|
|
305
|
+
}
|
|
306
|
+
return firstPage.data;
|
|
259
307
|
}
|
|
260
|
-
const promises =
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
...options,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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);
|
|
278
328
|
}
|
|
279
|
-
|
|
280
|
-
return firstPage?.data.concat(...followingPages.map((value) => value.data));
|
|
329
|
+
return allData;
|
|
281
330
|
}
|
|
282
331
|
URL(endpoint) {
|
|
283
332
|
return new URL(endpoint, this.base_url);
|
|
284
333
|
}
|
|
285
334
|
getOAuthUrl(options = {}) {
|
|
286
335
|
const redirectUri = options.redirect_uri || this.redirect_uri;
|
|
287
|
-
if (!redirectUri)
|
|
336
|
+
if (!redirectUri) {
|
|
288
337
|
throw new Error(`Missing redirect_uri parameter`);
|
|
289
|
-
|
|
338
|
+
}
|
|
290
339
|
const url = new URL(this.oauth_url);
|
|
291
340
|
url.searchParams.set("client_id", this.client_id);
|
|
292
341
|
url.searchParams.set("redirect_uri", redirectUri);
|
|
293
342
|
url.searchParams.set("response_type", "code");
|
|
294
343
|
url.searchParams.set("scope", this.scopes.join(" "));
|
|
295
|
-
|
|
344
|
+
const state = options.state ?? null;
|
|
345
|
+
if (state !== null) {
|
|
346
|
+
url.searchParams.set("state", state);
|
|
347
|
+
}
|
|
296
348
|
return { url: url.toString(), state };
|
|
297
349
|
}
|
|
298
350
|
async exchangeOAuthCode(code, redirect_uri) {
|
|
@@ -303,7 +355,7 @@ class FortytwoIntraClient {
|
|
|
303
355
|
redirect_uri: redirect_uri ? redirect_uri : this.redirect_uri,
|
|
304
356
|
code: code,
|
|
305
357
|
});
|
|
306
|
-
return res
|
|
358
|
+
return res.data;
|
|
307
359
|
}
|
|
308
360
|
async tokenInfos(options = {}) {
|
|
309
361
|
return this.get(this.token_info_url, options);
|
package/dist/lib/errors.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { AxiosError } from "axios";
|
|
2
2
|
export declare class FortytwoIntraClientError extends Error {
|
|
3
|
-
statusCode: number
|
|
4
|
-
statusText: string
|
|
5
|
-
data:
|
|
3
|
+
statusCode: number;
|
|
4
|
+
statusText: string;
|
|
5
|
+
data: unknown;
|
|
6
6
|
constructor(error: AxiosError);
|
|
7
7
|
}
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function isFortytwoIntraClientError(error: any): error is FortytwoIntraClientError;
|
|
8
|
+
export declare function isFortytwoIntraClientError(error: unknown): error is FortytwoIntraClientError;
|
package/dist/lib/errors.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FortytwoIntraClientError = void 0;
|
|
4
|
-
exports.simplifyAxiosError = simplifyAxiosError;
|
|
5
4
|
exports.isFortytwoIntraClientError = isFortytwoIntraClientError;
|
|
6
5
|
class FortytwoIntraClientError extends Error {
|
|
7
6
|
statusCode;
|
|
@@ -23,9 +22,6 @@ class FortytwoIntraClientError extends Error {
|
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
exports.FortytwoIntraClientError = FortytwoIntraClientError;
|
|
26
|
-
function simplifyAxiosError(error) {
|
|
27
|
-
return new FortytwoIntraClientError(error);
|
|
28
|
-
}
|
|
29
25
|
function isFortytwoIntraClientError(error) {
|
|
30
26
|
return error instanceof FortytwoIntraClientError;
|
|
31
27
|
}
|
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
|
-
}
|