@communecter/cocolight-api-client 1.0.7 → 1.0.9
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 +4 -4
- package/dist/22.cocolight-api-client.mjs.js +1 -0
- package/dist/320.cocolight-api-client.browser.js +1 -0
- package/dist/931.cocolight-api-client.browser.js +1 -0
- package/dist/931.cocolight-api-client.cjs +1 -0
- package/dist/cocolight-api-client.browser.js +7 -6
- package/dist/cocolight-api-client.browser.js.LICENSE.txt +1 -0
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/package.json +13 -4
- package/src/Api.js +12 -8
- package/src/ApiClient.js +118 -10
- package/src/api/EndpointApi.js +1534 -0
- package/src/api/News.js +286 -0
- package/src/api/Organization.js +162 -8
- package/src/api/Project.js +163 -8
- package/src/api/User.js +150 -9
- package/src/api/UserApi.js +2 -1
- package/src/endpoints.module.js +2 -2
- package/src/error.js +11 -0
- package/src/index.js +2 -1
- package/src/mixin/EntityMixin.js +48 -0
- package/src/mixin/NewsMixin.js +8 -0
- package/src/mixin/UserMixin.js +8 -0
- package/src/mixin/UtilMixin.js +246 -0
- package/src/utils/stream-utils.node.js +10 -0
- package/src/api/EntityMixin.js +0 -43
package/src/Api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Api.js
|
|
2
|
+
import EndpointApi from "./api/EndpointApi.js";
|
|
2
3
|
import { Organization } from "./api/Organization.js";
|
|
3
4
|
import { Project } from "./api/Project.js";
|
|
4
5
|
import { User } from "./api/User.js";
|
|
@@ -55,10 +56,9 @@ export default class Api {
|
|
|
55
56
|
if(error instanceof ApiClientError) {
|
|
56
57
|
if(error?.details?.error) {
|
|
57
58
|
throw new ApiAuthenticationError(error.details.error, error.status, error.details);
|
|
58
|
-
} else {
|
|
59
|
-
throw error;
|
|
60
59
|
}
|
|
61
60
|
}
|
|
61
|
+
throw error;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -80,7 +80,7 @@ export default class Api {
|
|
|
80
80
|
*/
|
|
81
81
|
me() {
|
|
82
82
|
if (!this._loggedUser) {
|
|
83
|
-
throw new
|
|
83
|
+
throw new ApiAuthenticationError("Accès refusé : utilisateur non authentifié.");
|
|
84
84
|
}
|
|
85
85
|
return this._loggedUser;
|
|
86
86
|
}
|
|
@@ -93,7 +93,7 @@ export default class Api {
|
|
|
93
93
|
*/
|
|
94
94
|
user(userData) {
|
|
95
95
|
try {
|
|
96
|
-
return new User(this._client, userData);
|
|
96
|
+
return new User(this._client, userData, {}, { EndpointApi });
|
|
97
97
|
} catch (error) {
|
|
98
98
|
console.error("[Api.user] Erreur lors de la création d'un objet utilisateur public :", error.message);
|
|
99
99
|
throw error;
|
|
@@ -112,9 +112,9 @@ export default class Api {
|
|
|
112
112
|
*/
|
|
113
113
|
async organization(organizationData, options = { getProfile: true }) {
|
|
114
114
|
try {
|
|
115
|
-
const oraganization = new Organization(this._client, organizationData);
|
|
115
|
+
const oraganization = new Organization(this._client, organizationData, { EndpointApi });
|
|
116
116
|
if (options.getProfile) {
|
|
117
|
-
await oraganization.
|
|
117
|
+
await oraganization.getProfil();
|
|
118
118
|
}
|
|
119
119
|
return oraganization;
|
|
120
120
|
} catch (error) {
|
|
@@ -134,9 +134,9 @@ export default class Api {
|
|
|
134
134
|
*/
|
|
135
135
|
async project(projectData, options = { getProfile: true }) {
|
|
136
136
|
try {
|
|
137
|
-
const project = new Project(this._client, projectData);
|
|
137
|
+
const project = new Project(this._client, projectData, { EndpointApi });
|
|
138
138
|
if (options.getProfile) {
|
|
139
|
-
await project.
|
|
139
|
+
await project.getProfil();
|
|
140
140
|
}
|
|
141
141
|
return project;
|
|
142
142
|
} catch (error) {
|
|
@@ -155,4 +155,8 @@ export default class Api {
|
|
|
155
155
|
return this._client;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
get endpointApi() {
|
|
159
|
+
return new EndpointApi(this._client);
|
|
160
|
+
}
|
|
161
|
+
|
|
158
162
|
}
|
package/src/ApiClient.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
|
|
3
|
+
import { AggregateAjvError } from "@segment/ajv-human-errors";
|
|
3
4
|
import Ajv from "ajv";
|
|
4
5
|
import addFormats from "ajv-formats";
|
|
5
6
|
import axios from "axios";
|
|
@@ -10,7 +11,7 @@ import pino from "pino";
|
|
|
10
11
|
|
|
11
12
|
import MongoID from "./EJSONType.js";
|
|
12
13
|
import endpointsJson from "./endpoints.module.js";
|
|
13
|
-
import { ApiClientError, CircuitBreakerError } from "./error.js";
|
|
14
|
+
import { ApiClientError, ApiResponseError, ApiValidationError, CircuitBreakerError } from "./error.js";
|
|
14
15
|
|
|
15
16
|
EJSON.addType("oid", value => {
|
|
16
17
|
return new MongoID.ObjectID(value);
|
|
@@ -74,9 +75,16 @@ export default class ApiClient extends EventEmitter {
|
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
// AJV
|
|
77
|
-
this._ajv = new Ajv({ strict: false, useDefaults: true });
|
|
78
|
+
this._ajv = new Ajv({ strict: false, useDefaults: true, allErrors: true, verbose: true });
|
|
78
79
|
addFormats(this._ajv);
|
|
79
80
|
|
|
81
|
+
this._ajv.addKeyword({
|
|
82
|
+
keyword: "startBeforeEnd",
|
|
83
|
+
type: "object",
|
|
84
|
+
errors: true,
|
|
85
|
+
validate: this._startBeforeEndValidate
|
|
86
|
+
});
|
|
87
|
+
|
|
80
88
|
// Pino logger
|
|
81
89
|
// (Ici en mode pretty-print sur la console, tu peux configurer comme tu veux)
|
|
82
90
|
this._logger = pino({
|
|
@@ -470,6 +478,15 @@ export default class ApiClient extends EventEmitter {
|
|
|
470
478
|
return obj;
|
|
471
479
|
}
|
|
472
480
|
|
|
481
|
+
/**
|
|
482
|
+
* Safely calls an asynchronous function and handles any errors that occur.
|
|
483
|
+
* Logs the error message using the instance's logger before re-throwing the error.
|
|
484
|
+
*
|
|
485
|
+
* @param {Function} fn - The asynchronous function to be called.
|
|
486
|
+
* @param {...any} args - The arguments to pass to the function.
|
|
487
|
+
* @returns {Promise<any>} The result of the asynchronous function.
|
|
488
|
+
* @throws {Error} Re-throws any error that occurs during the function execution.
|
|
489
|
+
*/
|
|
473
490
|
async safeCall(fn, ...args) {
|
|
474
491
|
try {
|
|
475
492
|
return await fn(...args);
|
|
@@ -543,8 +560,9 @@ export default class ApiClient extends EventEmitter {
|
|
|
543
560
|
const valid = validatePathParams(pathParams);
|
|
544
561
|
|
|
545
562
|
if (!valid) {
|
|
563
|
+
const errorMessages = this._ajvErrorHuman(validatePathParams.errors);
|
|
546
564
|
this.emit("validationError", { stage: "pathParams", errors: validatePathParams.errors });
|
|
547
|
-
throw new
|
|
565
|
+
throw new ApiValidationError("Path parameter validation failed.", 400, errorMessages, validatePathParams.errors);
|
|
548
566
|
}
|
|
549
567
|
|
|
550
568
|
resolvedParams = this._resolveSpecialValuesInPlace(pathParams);
|
|
@@ -571,8 +589,9 @@ export default class ApiClient extends EventEmitter {
|
|
|
571
589
|
const validateRequest = this._ajv.compile(schemaToCompile);
|
|
572
590
|
const valid = validateRequest(cleanedData);
|
|
573
591
|
if (!valid) {
|
|
592
|
+
const errorMessages = validateRequest.errors ? this._ajvErrorHuman(validateRequest.errors) : [];
|
|
574
593
|
this.emit("validationError", { stage: "request", errors: validateRequest.errors });
|
|
575
|
-
throw new
|
|
594
|
+
throw new ApiValidationError("Request validation failed.", 400, errorMessages , validateRequest.errors);
|
|
576
595
|
}
|
|
577
596
|
|
|
578
597
|
data = this._resolveSpecialValuesInPlace(cleanedData, resolvedParams);
|
|
@@ -603,8 +622,9 @@ export default class ApiClient extends EventEmitter {
|
|
|
603
622
|
const validateResponse = this._ajv.compile(schema);
|
|
604
623
|
const valid = validateResponse(response.data);
|
|
605
624
|
if (!valid) {
|
|
625
|
+
const errorMessages = this._ajvErrorHuman(validateResponse.errors);
|
|
606
626
|
this.emit("validationError", { stage: "response", errors: validateResponse.errors });
|
|
607
|
-
throw new
|
|
627
|
+
throw new ApiValidationError("Response validation failed.", status, errorMessages, validateResponse.errors);
|
|
608
628
|
}
|
|
609
629
|
}
|
|
610
630
|
}
|
|
@@ -666,14 +686,83 @@ export default class ApiClient extends EventEmitter {
|
|
|
666
686
|
} catch (error) {
|
|
667
687
|
this._updateCircuitBreakerError();
|
|
668
688
|
this._logger.error(`[ApiClient] Erreur lors de l'appel de ${constant}: ${error.message}`);
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
689
|
+
if(error instanceof ApiValidationError) {
|
|
690
|
+
throw error;
|
|
691
|
+
} else {
|
|
692
|
+
throw new ApiClientError(
|
|
693
|
+
`Erreur lors de l'appel de l'API : ${error.message}`,
|
|
694
|
+
error.response ? error.response.status : 500,
|
|
695
|
+
error.response ? error.response.data : null
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
674
699
|
}
|
|
675
700
|
}
|
|
676
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Converts AJV (Another JSON Schema Validator) errors into human-readable messages.
|
|
704
|
+
*
|
|
705
|
+
* @param {Array} errors - An array of AJV validation error objects.
|
|
706
|
+
* @returns {Array<string>} An array of human-readable error messages extracted from the AJV errors.
|
|
707
|
+
*/
|
|
708
|
+
_ajvErrorHuman(errors){
|
|
709
|
+
try {
|
|
710
|
+
const errorsMessages = new AggregateAjvError(errors);
|
|
711
|
+
const messages = errorsMessages.errors.map(({ message }) => message);
|
|
712
|
+
return messages;
|
|
713
|
+
} catch (e) {
|
|
714
|
+
this._logger.error("[ApiClient] _ajvErrorHuman", e);
|
|
715
|
+
return errors.map(({ message }) => message);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Checks the API response for errors and throws an `ApiResponseError` if any are found.
|
|
721
|
+
*
|
|
722
|
+
* This method examines the `response.data` object to determine if it contains
|
|
723
|
+
* error information. It handles both standard and nested error cases:
|
|
724
|
+
*
|
|
725
|
+
* - Standard case: If `data.result` is a boolean and is `false`, an error is thrown.
|
|
726
|
+
* - Nested case: If `data.resultErrors` exists and contains a `result` property
|
|
727
|
+
* that is a boolean and is `false`, an error is thrown.
|
|
728
|
+
*
|
|
729
|
+
* If no errors are found, the original `response` is returned.
|
|
730
|
+
*
|
|
731
|
+
* @param {Object} response - The API response object to check.
|
|
732
|
+
* @param {Object} response.data - The data payload of the response.
|
|
733
|
+
* @param {number} response.status - The HTTP status code of the response.
|
|
734
|
+
* @throws {ApiResponseError} If an error is detected in the response data.
|
|
735
|
+
* @returns {Object} The original `response` object if no errors are found.
|
|
736
|
+
*/
|
|
737
|
+
checkAndThrowApiResponseError(response) {
|
|
738
|
+
const data = response.data;
|
|
739
|
+
if (!data || typeof data !== "object") {
|
|
740
|
+
return response;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Cas standard : vérifie si data.result vaut false
|
|
744
|
+
if (typeof data.result === "boolean" && data.result === false) {
|
|
745
|
+
throw new ApiResponseError(
|
|
746
|
+
data.msg || "Erreur inconnue",
|
|
747
|
+
response.status,
|
|
748
|
+
data
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Cas imbriqué : vérifie si data.resultErrors existe et contient une erreur
|
|
753
|
+
if (data.resultErrors && typeof data.resultErrors === "object") {
|
|
754
|
+
if (typeof data.resultErrors.result === "boolean" && data.resultErrors.result === false) {
|
|
755
|
+
throw new ApiResponseError(
|
|
756
|
+
data.resultErrors.msg || "Erreur inconnue",
|
|
757
|
+
response.status,
|
|
758
|
+
data
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return response;
|
|
764
|
+
}
|
|
765
|
+
|
|
677
766
|
/**
|
|
678
767
|
* Réinitialise complètement la session de l'utilisateur :
|
|
679
768
|
* - Token d'accès
|
|
@@ -1271,5 +1360,24 @@ export default class ApiClient extends EventEmitter {
|
|
|
1271
1360
|
return data;
|
|
1272
1361
|
}
|
|
1273
1362
|
|
|
1363
|
+
_startBeforeEndValidate = (schema, data) => {
|
|
1364
|
+
if (!data.startDate || !data.endDate) return true;
|
|
1365
|
+
|
|
1366
|
+
const isValid = new Date(data.startDate) < new Date(data.endDate);
|
|
1367
|
+
if (!isValid) {
|
|
1368
|
+
this._startBeforeEndValidate.errors = [
|
|
1369
|
+
{
|
|
1370
|
+
instancePath: "", // ou "." si tu veux le chemin actuel
|
|
1371
|
+
schemaPath: "#/startBeforeEnd",
|
|
1372
|
+
keyword: "startBeforeEnd",
|
|
1373
|
+
params: {},
|
|
1374
|
+
message: "startDate must be before endDate"
|
|
1375
|
+
}
|
|
1376
|
+
];
|
|
1377
|
+
}
|
|
1378
|
+
return isValid;
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
|
|
1274
1382
|
|
|
1275
1383
|
}
|