@ibertran/fortytwo-intra-client 3.1.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ const client = new FortytwoIntraClient('your_client_id', 'your_client_secret');
17
17
 
18
18
 
19
19
  // Get a specific user by login
20
- const user = await client.get('users/jdoe');
20
+ const user = await client.get('users/ibertran');
21
21
  console.log(user);
22
22
  ```
23
23
 
@@ -32,16 +32,18 @@ The `FortytwoIntraClient` class accepts three parameters:
32
32
  ### Configuration Options
33
33
 
34
34
  ```typescript
35
- interface Conf {
36
- redirect_uri: string | null; // OAuth redirect URI (default: null)
37
- base_url: string; // API base URL (default: "https://api.intra.42.fr/v2/")
38
- token_url: string; // Token endpoint (default: "https://api.intra.42.fr/oauth/token")
39
- oauth_url: string; // OAuth endpoint (default: "https://api.intra.42.fr/oauth/authorize")
40
- scopes: string[]; // OAuth scopes (default: ["public"])
41
- rate: number; // Requests per second (default: 2)
42
- maxRetry: number; // Max retry attempts (default: 5)
43
- logs: boolean; // Enable logging (default: true)
44
- errors: boolean; // Enable error logging (default: true)
35
+ interface FortytwoIntraClientConf {
36
+ redirect_uri: string | null; // OAuth redirect URI (default: null)
37
+ base_url: string; // API base URL (default: "https://api.intra.42.fr/v2/")
38
+ token_url: string; // Token endpoint (default: "https://api.intra.42.fr/oauth/token")
39
+ oauth_url: string; // OAuth endpoint (default: "https://api.intra.42.fr/oauth/authorize")
40
+ token_info_url: string; // Token info endpoint (default: "https://api.intra.42.fr/oauth/token/info")
41
+ scopes: string[]; // OAuth scopes (default: ["public"])
42
+ rateLimitMaxRequests: number; // Max requests per time window (default: 2)
43
+ rateLimitPerMilliseconds: number; // Time window in milliseconds (default: 1200)
44
+ maxRetry: number; // Max retry attempts (default: 5)
45
+ logLine: boolean; // Enable request logging (default: true)
46
+ errLogBody: boolean; // Log error response bodies (default: true)
45
47
  }
46
48
  ```
47
49
 
@@ -49,10 +51,12 @@ interface Conf {
49
51
 
50
52
  ```typescript
51
53
  const client = new FortytwoIntraClient('your_client_id', 'your_client_secret', {
52
- rate: 1, // 1 request per second
53
- maxRetry: 3, // Retry up to 3 times
54
- logs: false, // Disable logging
55
- scopes: ['public', 'projects'] // Extended scopes
54
+ rateLimitMaxRequests: 1, // 1 request per 1200ms
55
+ rateLimitPerMilliseconds: 1200, // Time window
56
+ maxRetry: 3, // Retry up to 3 times
57
+ logLine: false, // Disable logging
58
+ errLogBody: false, // Disable error body logging
59
+ scopes: ['public', 'projects'] // Extended scopes
56
60
  });
57
61
  ```
58
62
 
@@ -189,16 +193,16 @@ Requests are automatically throttled based on the configured rate limit. The def
189
193
 
190
194
  ## Logging
191
195
 
192
- By default, the library logs all requests with status codes:
196
+ By default, the library logs all requests with colored status codes and formatted parameters:
193
197
 
194
198
  ```
195
- ✅ 200 GET https://api.intra.42.fr/v2/users/jdoe
196
- ✅ 200 GET https://api.intra.42.fr/v2/campus/1
197
- 🔄 429 GET https://api.intra.42.fr/v2/projects retry 1/5
198
- ✅ 200 GET https://api.intra.42.fr/v2/projects retry 1/5
199
+ 200 GET /v2/users/jdoe
200
+ 200 GET /v2/campus { filter: { city: 'lyon' } }
201
+ 429 GET /v2/projects
202
+ 200 GET /v2/projects retry 1/5
199
203
  ```
200
204
 
201
- Disable logging by setting `logs: false` in the configuration.
205
+ Disable logging by setting `logLine: false` in the configuration. Control error body logging with `errLogBody: false`.
202
206
 
203
207
  ## TypeScript Support
204
208
 
@@ -1 +1,9 @@
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;
1
9
  export {};
package/dist/HttpError.js CHANGED
@@ -1,23 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- // class HttpError extends Error {
4
- // status: number | null;
5
- // url: string | null;
6
- // details: any;
7
- // constructor(err: any) {
8
- // super(err.message || "Unknown API error");
9
- // this.name = "HttpError";
10
- // this.status = err.status || err.response?.status || null;
11
- // this.url = err.config?.url || err.request?.responseURL || null;
12
- // this.details = err.response?.data || null;
13
- // }
14
- // }
15
- // export function simplifyAxiosError(err: any): HttpError {
16
- // if (!err) {
17
- // return new HttpError({ message: "Unknown error" });
18
- // }
19
- // return new HttpError(err);
20
- // }
21
- // export function isAxiosError(err: any): err is AxiosError {
22
- // return err && err.isAxiosError === true;
23
- // }
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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { inputOptions } from "./types";
2
+ import { FortytwoIntraClientError, isFortytwoIntraClientError } from "./lib/errors";
2
3
  export interface FortytwoIntraClientConf {
3
4
  redirect_uri: string | null;
4
5
  base_url: string;
@@ -54,3 +55,4 @@ export declare class FortytwoIntraClient {
54
55
  exchangeOAuthCode(code: string, redirect_uri?: string): Promise<any>;
55
56
  tokenInfos(options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
56
57
  }
58
+ export { FortytwoIntraClientError, isFortytwoIntraClientError };
package/dist/index.js CHANGED
@@ -3,11 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.FortytwoIntraClient = void 0;
6
+ exports.isFortytwoIntraClientError = exports.FortytwoIntraClientError = exports.FortytwoIntraClient = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const axios_rate_limit_1 = __importDefault(require("axios-rate-limit"));
9
9
  const pagination_1 = require("./lib/pagination");
10
10
  const logs_1 = require("./lib/logs");
11
+ const errors_1 = require("./lib/errors");
12
+ Object.defineProperty(exports, "FortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.FortytwoIntraClientError; } });
13
+ Object.defineProperty(exports, "isFortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.isFortytwoIntraClientError; } });
11
14
  const crypto_1 = __importDefault(require("crypto"));
12
15
  const defaultConf = {
13
16
  redirect_uri: null,
@@ -124,8 +127,11 @@ class FortytwoIntraClient {
124
127
  return this.reqHandler(url, options);
125
128
  }
126
129
  else {
127
- if (this.throwOnError)
128
- throw err;
130
+ const clientError = (0, errors_1.simplifyAxiosError)(err);
131
+ if (this.throwOnError) {
132
+ throw clientError;
133
+ }
134
+ return clientError;
129
135
  }
130
136
  }
131
137
  else {
@@ -229,7 +235,7 @@ class FortytwoIntraClient {
229
235
  }
230
236
  const perPage = options.perPage || 100;
231
237
  let url = new URL(endpoint);
232
- const initialRes = await this.reqHandler(url, {
238
+ const firstPage = await this.reqHandler(url, {
233
239
  method: "GET",
234
240
  attempt: 0,
235
241
  currpage: 1,
@@ -246,10 +252,10 @@ class FortytwoIntraClient {
246
252
  });
247
253
  let lastPage;
248
254
  try {
249
- lastPage = Math.min((0, pagination_1.getLastPage)(initialRes.headers["link"]), options.maxPages || Infinity);
255
+ lastPage = Math.min((0, pagination_1.getLastPage)(firstPage.headers["link"]), options.maxPages || Infinity);
250
256
  }
251
257
  catch (err) {
252
- return initialRes?.data;
258
+ return firstPage?.data;
253
259
  }
254
260
  const promises = [];
255
261
  for (let i = 2; i <= lastPage; i++) {
@@ -270,9 +276,8 @@ class FortytwoIntraClient {
270
276
  }
271
277
  }));
272
278
  }
273
- return Promise.all(promises).then((values) => {
274
- return initialRes?.data.concat(...values.map((value) => value.data));
275
- });
279
+ const followingPages = await Promise.all(promises);
280
+ return firstPage?.data.concat(...followingPages.map((value) => value.data));
276
281
  }
277
282
  URL(endpoint) {
278
283
  return new URL(endpoint, this.base_url);
@@ -0,0 +1,9 @@
1
+ import { AxiosError } from "axios";
2
+ export declare class FortytwoIntraClientError extends Error {
3
+ statusCode: number | null;
4
+ statusText: string | null;
5
+ data: any;
6
+ constructor(error: AxiosError);
7
+ }
8
+ export declare function simplifyAxiosError(error: AxiosError): FortytwoIntraClientError;
9
+ export declare function isFortytwoIntraClientError(error: any): error is FortytwoIntraClientError;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FortytwoIntraClientError = void 0;
4
+ exports.simplifyAxiosError = simplifyAxiosError;
5
+ exports.isFortytwoIntraClientError = isFortytwoIntraClientError;
6
+ class FortytwoIntraClientError extends Error {
7
+ statusCode;
8
+ statusText;
9
+ data;
10
+ constructor(error) {
11
+ const method = error.config?.method?.toUpperCase() ?? null;
12
+ const route = error.config?.url ?? null;
13
+ const statusCode = error.response?.status ?? NaN;
14
+ const statusText = error.response?.statusText ?? "Unknown";
15
+ const message = method && route
16
+ ? `${method} ${route} - HTTP ${statusCode} ${statusText}`
17
+ : error.message;
18
+ super(message);
19
+ this.name = "FortytwoIntraClientError";
20
+ this.statusCode = statusCode;
21
+ this.statusText = statusText;
22
+ this.data = error.response?.data;
23
+ }
24
+ }
25
+ exports.FortytwoIntraClientError = FortytwoIntraClientError;
26
+ function simplifyAxiosError(error) {
27
+ return new FortytwoIntraClientError(error);
28
+ }
29
+ function isFortytwoIntraClientError(error) {
30
+ return error instanceof FortytwoIntraClientError;
31
+ }
package/dist/lib/logs.js CHANGED
@@ -2,12 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLogLine = getLogLine;
4
4
  exports.getErrorLogLine = getErrorLogLine;
5
+ const util_1 = require("util");
5
6
  const green = "\x1b[42m";
6
7
  const red = "\x1b[41m";
7
8
  const reset = "\x1b[0m";
9
+ function formatQueryParams(params) {
10
+ const { per_page, page, ...filteredParams } = params;
11
+ const keys = Object.keys(filteredParams);
12
+ if (keys.length === 0)
13
+ return "";
14
+ return (0, util_1.inspect)(filteredParams, {
15
+ depth: Infinity,
16
+ compact: true,
17
+ breakLength: Infinity,
18
+ colors: true
19
+ });
20
+ }
8
21
  function getLogLine(res, options) {
9
22
  const tokens = [];
10
- // Get query parameters as object
11
23
  const queryParams = res.config?.params || {};
12
24
  tokens.push(`${green}${res.status}${reset}`);
13
25
  tokens.push(options.method.padEnd(6, " "));
@@ -16,20 +28,19 @@ function getLogLine(res, options) {
16
28
  const url = new URL(res.config.url);
17
29
  tokens.push(url.pathname);
18
30
  }
19
- // Add query parameters as object if they exist
20
- if (Object.keys(queryParams).length > 0) {
21
- delete queryParams.per_page;
22
- delete queryParams.page;
23
- tokens.push(JSON.stringify(queryParams, null, 1));
31
+ // Add query parameters
32
+ const formattedParams = formatQueryParams(queryParams);
33
+ if (formattedParams) {
34
+ tokens.push(formattedParams);
24
35
  }
36
+ // Add pagination info
25
37
  if (options.currpage) {
26
- tokens.push(`| page: ${options.currpage}/${options.lastPage === Infinity ? "unknown" : options.lastPage}`);
38
+ tokens.push(`| ${options.currpage}/${options.lastPage === Infinity ? "..." : options.lastPage}`);
27
39
  }
28
40
  return tokens.join(" ");
29
41
  }
30
42
  function getErrorLogLine(err, options) {
31
43
  const tokens = [];
32
- // Get query parameters as object
33
44
  const queryParams = err.config?.params || {};
34
45
  tokens.push(`${red}${err.status}${reset}`);
35
46
  tokens.push(options.method.padEnd(6, " "));
@@ -38,9 +49,10 @@ function getErrorLogLine(err, options) {
38
49
  const url = new URL(err.config.url);
39
50
  tokens.push(url.pathname);
40
51
  }
41
- // Add query parameters as object if they exist
42
- if (Object.keys(queryParams).length > 0) {
43
- tokens.push(JSON.stringify(queryParams));
52
+ // Add query parameters
53
+ const formattedParams = formatQueryParams(queryParams);
54
+ if (formattedParams) {
55
+ tokens.push(formattedParams);
44
56
  }
45
57
  return tokens.join(" ");
46
58
  }
package/dist/utils.d.ts CHANGED
@@ -1 +1,4 @@
1
+ import * as superagent from "superagent";
2
+ import { Method } from "./types";
3
+ export declare function initRequest(method: Method, url: URL): superagent.Request;
1
4
  export declare function getLastPage(header: string | undefined): number;
package/dist/utils.js CHANGED
@@ -1,6 +1,56 @@
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
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initRequest = initRequest;
3
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
+ }
4
54
  function getLastPage(header) {
5
55
  if (!header) {
6
56
  throw Error("No pagination link header");
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@ibertran/fortytwo-intra-client",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
+ "author": {
7
+ "name": "Ian BERTRAND"
8
+ },
6
9
  "scripts": {
7
10
  "build": "tsc"
8
11
  },
@@ -12,8 +15,8 @@
12
15
  },
13
16
  "devDependencies": {
14
17
  "typescript": "^5.9.3",
15
- "dotenv": "^16.6.1",
16
18
  "ts-node": "^10.9.2",
19
+ "dotenv": "^16.6.1",
17
20
  "express": "^4.18.2"
18
21
  },
19
22
  "files": [
package/dist/log.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import { reqOptions } from "./types";
2
- import { AxiosResponse, AxiosError } from "axios";
3
- export declare function getLogLine(res: AxiosResponse, options: reqOptions): string;
4
- export declare function getErrorLogLine(err: AxiosError, options: reqOptions): string;
package/dist/log.js DELETED
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLogLine = getLogLine;
4
- exports.getErrorLogLine = getErrorLogLine;
5
- const green = "\x1b[42m";
6
- const red = "\x1b[41m";
7
- const reset = "\x1b[0m";
8
- function getLogLine(res, options) {
9
- const tokens = [];
10
- // Get query parameters as object
11
- const queryParams = res.config?.params || {};
12
- if (process.stdout.isTTY) {
13
- tokens.push(`${green}${res.status}${reset}`);
14
- }
15
- else {
16
- tokens.push(`${res.status}`);
17
- }
18
- tokens.push(options.method.padEnd(6, " "));
19
- // Add the base URL path
20
- if (res.config?.url) {
21
- const url = new URL(res.config.url);
22
- tokens.push(url.pathname);
23
- }
24
- // Add query parameters as object if they exist
25
- if (Object.keys(queryParams).length > 0) {
26
- tokens.push(JSON.stringify(queryParams));
27
- }
28
- return tokens.join(" ");
29
- }
30
- function getErrorLogLine(err, options) {
31
- const tokens = [];
32
- // Get query parameters as object
33
- const queryParams = err.config?.params || {};
34
- if (process.stdout.isTTY) {
35
- tokens.push(`${red}${err.status}${reset}`);
36
- }
37
- else {
38
- tokens.push(`${err.status}`);
39
- }
40
- tokens.push(options.method.padEnd(6, " "));
41
- // Add the base URL path
42
- if (err.config?.url) {
43
- const url = new URL(err.config.url);
44
- tokens.push(url.pathname);
45
- }
46
- // Add query parameters as object if they exist
47
- if (Object.keys(queryParams).length > 0) {
48
- tokens.push(JSON.stringify(queryParams));
49
- }
50
- return tokens.join(" ");
51
- }