@communecter/cocolight-api-client 1.0.8 → 1.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@communecter/cocolight-api-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Client Axios simplifié pour l'API cocolight",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,7 +32,8 @@
32
32
  "generate:testdata": "node ./scripts/generate-test-data.js",
33
33
  "generate:reponses": "node ./scripts/generate-constant-response-200.js",
34
34
  "generate:methodeapi": "node ./scripts/generate-methode-api.js",
35
- "generate:ajv-standalone": "node ./scripts/generate-validate-function-ajv.js"
35
+ "generate:ajv-standalone": "node ./scripts/generate-validate-function-ajv.js",
36
+ "generate:entities": "node scripts/generate-entities.js"
36
37
  },
37
38
  "keywords": [
38
39
  "communecter",
@@ -60,7 +61,8 @@
60
61
  "jwt-decode": "^4.0.0",
61
62
  "pino": "^9.6.0",
62
63
  "pino-pretty": "^13.0.0",
63
- "file-type": "^20.4.1"
64
+ "file-type": "^20.4.1",
65
+ "bson-objectid": "^2.0.4"
64
66
  },
65
67
  "devDependencies": {
66
68
  "@babel/core": "^7.26.10",
@@ -72,6 +74,7 @@
72
74
  "axios": "^1.4.0",
73
75
  "axios-retry": "^4.5.0",
74
76
  "file-type": "^20.4.1",
77
+ "bson-objectid": "^2.0.4",
75
78
  "babel-jest": "^29.7.0",
76
79
  "babel-loader": "^10.0.0",
77
80
  "ejson": "^2.2.3",
package/src/Api.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // Api.js
2
+ import EndpointApi from "./api/EndpointApi.js";
3
+ import { News } from "./api/News.js";
2
4
  import { Organization } from "./api/Organization.js";
3
5
  import { Project } from "./api/Project.js";
4
6
  import { User } from "./api/User.js";
@@ -75,12 +77,14 @@ export default class Api {
75
77
  /**
76
78
  * Retourne l'utilisateur connecté.
77
79
  *
78
- * @returns {User} L'utilisateur connecté.
80
+ * @returns {Promise<User>} L'utilisateur connecté.
81
+ * @throws {ApiAuthenticationError} Si l'utilisateur n'est pas authentifié.
79
82
  */
80
- me() {
83
+ async me() {
81
84
  if (!this._loggedUser) {
82
85
  throw new ApiAuthenticationError("Accès refusé : utilisateur non authentifié.");
83
86
  }
87
+ await this._loggedUser.get();
84
88
  return this._loggedUser;
85
89
  }
86
90
 
@@ -88,11 +92,18 @@ export default class Api {
88
92
  * Crée une instance User pour un utilisateur donné (autre que le connecté).
89
93
  *
90
94
  * @param {Object} userData - Les données de l'utilisateur public.
91
- * @returns {User}
95
+ * @returns {Promise<User>} Une promesse qui résout l'instance User.
96
+ * @throws {Error} Si une erreur se produit lors de la création de l'utilisateur.
92
97
  */
93
- user(userData) {
98
+ async user(userData) {
94
99
  try {
95
- return new User(this._client, userData);
100
+ if (!userData.id && !userData.slug) {
101
+ return new User(this._client, userData, { EndpointApi, Organization, Project, News });
102
+ } else {
103
+ const user = new User(this._client, userData, { EndpointApi, Organization, Project, News });
104
+ await user.get();
105
+ return user;
106
+ }
96
107
  } catch (error) {
97
108
  console.error("[Api.user] Erreur lors de la création d'un objet utilisateur public :", error.message);
98
109
  throw error;
@@ -104,18 +115,17 @@ export default class Api {
104
115
  * Creates an Organization object and optionally retrieves its profile.
105
116
  *
106
117
  * @param {Object} organizationData - The data required to initialize the Organization object.
107
- * @param {Object} [options={ getProfile: true }] - Additional options for the organization creation.
108
- * @param {boolean} [options.getProfile=true] - Whether to fetch the organization's profile after creation.
109
118
  * @returns {Promise<Organization>} A promise that resolves to the created Organization object.
110
119
  * @throws {Error} Throws an error if the organization creation or profile retrieval fails.
111
120
  */
112
- async organization(organizationData, options = { getProfile: true }) {
121
+ async organization(organizationData) {
113
122
  try {
114
- const oraganization = new Organization(this._client, organizationData);
115
- if (options.getProfile) {
116
- await oraganization.getProfil();
123
+ const organization = new Organization(this._client, organizationData, { EndpointApi, User, Project, News });
124
+ if (!organizationData.id && !organizationData.slug) {
125
+ throw new Error("Vous devez fournir un id ou un slug pour créer une instance Organization.");
117
126
  }
118
- return oraganization;
127
+ await organization.get();
128
+ return organization;
119
129
  } catch (error) {
120
130
  console.error("[Api.organization] Erreur lors de la création d'un objet organisation :", error.message);
121
131
  throw error;
@@ -126,17 +136,16 @@ export default class Api {
126
136
  * Creates a new Project instance and optionally retrieves its profile.
127
137
  *
128
138
  * @param {Object} projectData - The data used to initialize the Project instance.
129
- * @param {Object} [options={ getProfile: true }] - Additional options for project creation.
130
- * @param {boolean} [options.getProfile=true] - Whether to retrieve the project's profile after creation.
131
139
  * @returns {Promise<Project>} A promise that resolves to the created Project instance.
132
140
  * @throws {Error} If an error occurs during project creation or profile retrieval.
133
141
  */
134
- async project(projectData, options = { getProfile: true }) {
142
+ async project(projectData) {
135
143
  try {
136
- const project = new Project(this._client, projectData);
137
- if (options.getProfile) {
138
- await project.getProfil();
144
+ const project = new Project(this._client, projectData, { User, News, EndpointApi });
145
+ if (!projectData.id && !projectData.slug) {
146
+ throw new Error("Vous devez fournir un id ou un slug pour créer une instance Project.");
139
147
  }
148
+ await project.get();
140
149
  return project;
141
150
  } catch (error) {
142
151
  console.error("[Api.project] Erreur lors de la création d'un objet projet :", error.message);
@@ -149,9 +158,17 @@ export default class Api {
149
158
  *
150
159
  * @returns {ApiClient}
151
160
  */
152
-
153
161
  get client() {
154
162
  return this._client;
155
163
  }
156
164
 
165
+ /**
166
+ * Retourne l'instance d'EndpointApi.
167
+ *
168
+ * @returns {EndpointApi}
169
+ */
170
+ get endpointApi() {
171
+ return new EndpointApi(this._client);
172
+ }
173
+
157
174
  }
package/src/ApiClient.js CHANGED
@@ -51,6 +51,8 @@ export default class ApiClient extends EventEmitter {
51
51
  throw new ApiClientError("Le paramètre \"baseURL\" est obligatoire.", 500);
52
52
  }
53
53
 
54
+ this.__entityTag = "ApiClient";
55
+
54
56
  this._baseURL = baseURL;
55
57
  this._refreshToken = refreshToken;
56
58
  this._refreshUrl = refreshUrl;
@@ -78,8 +80,14 @@ export default class ApiClient extends EventEmitter {
78
80
  this._ajv = new Ajv({ strict: false, useDefaults: true, allErrors: true, verbose: true });
79
81
  addFormats(this._ajv);
80
82
 
83
+ this._ajv.addKeyword({
84
+ keyword: "startBeforeEnd",
85
+ type: "object",
86
+ errors: true,
87
+ validate: this._startBeforeEndValidate
88
+ });
89
+
81
90
  // Pino logger
82
- // (Ici en mode pretty-print sur la console, tu peux configurer comme tu veux)
83
91
  this._logger = pino({
84
92
  transport: {
85
93
  target: "pino-pretty",
@@ -582,9 +590,9 @@ export default class ApiClient extends EventEmitter {
582
590
  const validateRequest = this._ajv.compile(schemaToCompile);
583
591
  const valid = validateRequest(cleanedData);
584
592
  if (!valid) {
585
- const errorMessages = this._ajvErrorHuman(validateRequest.errors);
593
+ const errorMessages = validateRequest.errors ? this._ajvErrorHuman(validateRequest.errors) : [];
586
594
  this.emit("validationError", { stage: "request", errors: validateRequest.errors });
587
- throw new ApiValidationError("Request validation failed.", 400, errorMessages, validateRequest.errors);
595
+ throw new ApiValidationError("Request validation failed.", 400, errorMessages , validateRequest.errors);
588
596
  }
589
597
 
590
598
  data = this._resolveSpecialValuesInPlace(cleanedData, resolvedParams);
@@ -679,11 +687,16 @@ export default class ApiClient extends EventEmitter {
679
687
  } catch (error) {
680
688
  this._updateCircuitBreakerError();
681
689
  this._logger.error(`[ApiClient] Erreur lors de l'appel de ${constant}: ${error.message}`);
682
- throw new ApiClientError(
683
- `Erreur lors de l'appel de l'API : ${error.message}`,
684
- error.response ? error.response.status : 500,
685
- error.response ? error.response.data : null
686
- );
690
+ if(error instanceof ApiValidationError) {
691
+ throw error;
692
+ } else {
693
+ throw new ApiClientError(
694
+ `Erreur lors de l'appel de l'API : ${error.message}`,
695
+ error.response ? error.response.status : 500,
696
+ error.response ? error.response.data : null
697
+ );
698
+ }
699
+
687
700
  }
688
701
  }
689
702
 
@@ -694,9 +707,14 @@ export default class ApiClient extends EventEmitter {
694
707
  * @returns {Array<string>} An array of human-readable error messages extracted from the AJV errors.
695
708
  */
696
709
  _ajvErrorHuman(errors){
697
- const errorsMessages = new AggregateAjvError(errors);
698
- const messages = errorsMessages.errors.map(({ message }) => message);
699
- return messages;
710
+ try {
711
+ const errorsMessages = new AggregateAjvError(errors);
712
+ const messages = errorsMessages.errors.map(({ message }) => message);
713
+ return messages;
714
+ } catch (e) {
715
+ this._logger.error("[ApiClient] _ajvErrorHuman", e);
716
+ return errors.map(({ message }) => message);
717
+ }
700
718
  }
701
719
 
702
720
  /**
@@ -1343,5 +1361,32 @@ export default class ApiClient extends EventEmitter {
1343
1361
  return data;
1344
1362
  }
1345
1363
 
1364
+ _startBeforeEndValidate = (schema, data) => {
1365
+ if (!data.startDate || !data.endDate) return true;
1366
+
1367
+ const isValid = new Date(data.startDate) < new Date(data.endDate);
1368
+ if (!isValid) {
1369
+ this._startBeforeEndValidate.errors = [
1370
+ {
1371
+ instancePath: "", // ou "." si tu veux le chemin actuel
1372
+ schemaPath: "#/startBeforeEnd",
1373
+ keyword: "startBeforeEnd",
1374
+ params: {},
1375
+ message: "startDate must be before endDate"
1376
+ }
1377
+ ];
1378
+ }
1379
+ return isValid;
1380
+ };
1381
+
1382
+
1383
+ getRequestSchema(constant) {
1384
+ const endpoint = this._endpoints.find(e => e.constant === constant);
1385
+ return endpoint?.request || null;
1386
+ }
1387
+
1388
+ getPathSchema(constant) {
1389
+ return this._endpoints.find(e => e.constant === constant)?.pathParams || null;
1390
+ }
1346
1391
 
1347
1392
  }