@communecter/cocolight-api-client 1.0.5 → 1.0.7
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/dist/cocolight-api-client.browser.js +3 -3
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/package.json +18 -14
- package/src/Api.js +158 -0
- package/src/ApiClient.js +62 -4
- package/src/api/EntityMixin.js +43 -0
- package/src/api/Organization.js +79 -0
- package/src/api/Project.js +79 -0
- package/src/api/User.js +89 -0
- package/src/api/UserApi.js +48 -0
- package/src/endpoints.module.js +2 -2
- package/src/error.js +41 -4
- package/src/index.js +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@communecter/cocolight-api-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Client Axios simplifié pour l'API cocolight",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --config jest.config.js",
|
|
19
19
|
"exemple": "node exemple/exemple.js",
|
|
20
|
+
"exemple-api": "node exemple/exemple-api.js",
|
|
20
21
|
"lint": "eslint ./src ./exemple ./scripts --ext .js",
|
|
21
22
|
"lint:fix": "eslint ./src ./scripts --ext .js --fix",
|
|
22
23
|
"build:browser": "webpack --config webpack.config.standalone.js",
|
|
23
24
|
"build:node": "webpack --config webpack.config.node.js",
|
|
24
25
|
"build:esm": "webpack --config webpack.config.esm.mjs",
|
|
25
|
-
"build": "npm run generate:module && npm run generate:doc && npm run lint:fix && npm run build:browser && npm run build:node && npm run build:esm",
|
|
26
|
+
"build": "npm run generate:module:publish && npm run generate:doc && npm run lint:fix && npm run build:browser && npm run build:node && npm run build:esm",
|
|
26
27
|
"build:publish": "npm run build && npm publish --access public",
|
|
27
28
|
"generate:module": "node ./scripts/transform-json-module.js",
|
|
29
|
+
"generate:module:publish": "node ./scripts/transform-json-module.publish.js",
|
|
28
30
|
"generate:doc": "node ./scripts/generate-doc.js",
|
|
29
31
|
"generate:test": "node ./scripts/generate-tests.js",
|
|
30
32
|
"generate:testdata": "node ./scripts/generate-test-data.js"
|
|
@@ -39,10 +41,10 @@
|
|
|
39
41
|
"author": "Thomas",
|
|
40
42
|
"license": "MIT",
|
|
41
43
|
"files": [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
"dist/",
|
|
45
|
+
"src/",
|
|
46
|
+
"package.json",
|
|
47
|
+
"README.md"
|
|
46
48
|
],
|
|
47
49
|
"peerDependencies": {
|
|
48
50
|
"ajv": "^8.17.1",
|
|
@@ -52,27 +54,29 @@
|
|
|
52
54
|
"ejson": "^2.2.3",
|
|
53
55
|
"events": "^3.3.0",
|
|
54
56
|
"pino": "^9.6.0",
|
|
55
|
-
"pino-pretty": "^13.0.0"
|
|
57
|
+
"pino-pretty": "^13.0.0",
|
|
58
|
+
"jwt-decode": "^4.0.0"
|
|
56
59
|
},
|
|
57
60
|
"devDependencies": {
|
|
61
|
+
"@babel/core": "^7.26.10",
|
|
62
|
+
"@babel/preset-env": "^7.26.9",
|
|
63
|
+
"@eslint/js": "^9.23.0",
|
|
58
64
|
"ajv": "^8.17.1",
|
|
59
65
|
"ajv-formats": "^3.0.1",
|
|
60
66
|
"axios": "^1.4.0",
|
|
61
67
|
"axios-retry": "^4.5.0",
|
|
62
|
-
"
|
|
63
|
-
"events": "^3.3.0",
|
|
64
|
-
"pino": "^9.6.0",
|
|
65
|
-
"pino-pretty": "^13.0.0",
|
|
66
|
-
"@babel/core": "^7.26.10",
|
|
67
|
-
"@babel/preset-env": "^7.26.9",
|
|
68
|
-
"@eslint/js": "^9.23.0",
|
|
68
|
+
"jwt-decode": "^4.0.0",
|
|
69
69
|
"babel-jest": "^29.7.0",
|
|
70
70
|
"babel-loader": "^10.0.0",
|
|
71
|
+
"ejson": "^2.2.3",
|
|
71
72
|
"eslint": "^9.23.0",
|
|
72
73
|
"eslint-plugin-import": "^2.31.0",
|
|
74
|
+
"events": "^3.3.0",
|
|
73
75
|
"globals": "^16.0.0",
|
|
74
76
|
"jest": "^29.7.0",
|
|
75
77
|
"nodemon": "^3.1.9",
|
|
78
|
+
"pino": "^9.6.0",
|
|
79
|
+
"pino-pretty": "^13.0.0",
|
|
76
80
|
"webpack": "^5.98.0",
|
|
77
81
|
"webpack-cli": "^6.0.1"
|
|
78
82
|
}
|
package/src/Api.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Api.js
|
|
2
|
+
import { Organization } from "./api/Organization.js";
|
|
3
|
+
import { Project } from "./api/Project.js";
|
|
4
|
+
import { User } from "./api/User.js";
|
|
5
|
+
import { UserApi } from "./api/UserApi.js";
|
|
6
|
+
import { ApiAuthenticationError, ApiClientError } from "./error.js";
|
|
7
|
+
|
|
8
|
+
export default class Api {
|
|
9
|
+
/**
|
|
10
|
+
* Authentifie l'utilisateur et retourne une instance d'Api.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} email - L'adresse email.
|
|
13
|
+
* @param {string} password - Le mot de passe.
|
|
14
|
+
* @param {Object} options - Options pour l'ApiClient (baseURL, debug, etc.)
|
|
15
|
+
* @returns {Promise<Api>}
|
|
16
|
+
*/
|
|
17
|
+
static async userLogin(email, password, options) {
|
|
18
|
+
const userApi = Api.userApi(options);
|
|
19
|
+
return await Api.userApiLogin(userApi, email, password);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates an instance of the UserApi class with the provided options.
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} options - Configuration options for initializing the UserApi instance.
|
|
26
|
+
* @returns {UserApi} An instance of the UserApi class.
|
|
27
|
+
* @throws {Error} Throws an error if the UserApi instance cannot be created.
|
|
28
|
+
*/
|
|
29
|
+
static userApi(options) {
|
|
30
|
+
try {
|
|
31
|
+
const userApi = new UserApi(options);
|
|
32
|
+
return userApi;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("[Api.userApi] Erreur lors de la création d'un objet utilisateur :", error.message);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Logs in a user using the provided userApi instance, email, and password.
|
|
41
|
+
*
|
|
42
|
+
* @param {Object} userApi - The API instance used for user authentication.
|
|
43
|
+
* @param {string} email - The email address of the user.
|
|
44
|
+
* @param {string} password - The password of the user.
|
|
45
|
+
* @returns {Promise<Api>} A promise that resolves to an instance of the Api class
|
|
46
|
+
* initialized with the logged-in user and the API client.
|
|
47
|
+
* @throws {ApiAuthenticationError} If the login fails due to authentication issues.
|
|
48
|
+
* @throws {Error} If an unexpected error occurs during the login process.
|
|
49
|
+
*/
|
|
50
|
+
static async userApiLogin(userApi, email, password) {
|
|
51
|
+
try {
|
|
52
|
+
const loggedUser = await userApi.login(email, password);
|
|
53
|
+
return new Api(loggedUser, userApi.client);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if(error instanceof ApiClientError) {
|
|
56
|
+
if(error?.details?.error) {
|
|
57
|
+
throw new ApiAuthenticationError(error.details.error, error.status, error.details);
|
|
58
|
+
} else {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Construit une instance d'Api.
|
|
67
|
+
*
|
|
68
|
+
* @param {User|null} loggedUser - L'utilisateur connecté ou null.
|
|
69
|
+
* @param {ApiClient} client - L'instance d'ApiClient.
|
|
70
|
+
*/
|
|
71
|
+
constructor(loggedUser, client) {
|
|
72
|
+
this._loggedUser = loggedUser;
|
|
73
|
+
this._client = client;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Retourne l'utilisateur connecté.
|
|
78
|
+
*
|
|
79
|
+
* @returns {User} L'utilisateur connecté.
|
|
80
|
+
*/
|
|
81
|
+
me() {
|
|
82
|
+
if (!this._loggedUser) {
|
|
83
|
+
throw new Error("Accès refusé : utilisateur non authentifié.");
|
|
84
|
+
}
|
|
85
|
+
return this._loggedUser;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Crée une instance User pour un utilisateur donné (autre que le connecté).
|
|
90
|
+
*
|
|
91
|
+
* @param {Object} userData - Les données de l'utilisateur public.
|
|
92
|
+
* @returns {User}
|
|
93
|
+
*/
|
|
94
|
+
user(userData) {
|
|
95
|
+
try {
|
|
96
|
+
return new User(this._client, userData);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("[Api.user] Erreur lors de la création d'un objet utilisateur public :", error.message);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates an Organization object and optionally retrieves its profile.
|
|
106
|
+
*
|
|
107
|
+
* @param {Object} organizationData - The data required to initialize the Organization object.
|
|
108
|
+
* @param {Object} [options={ getProfile: true }] - Additional options for the organization creation.
|
|
109
|
+
* @param {boolean} [options.getProfile=true] - Whether to fetch the organization's profile after creation.
|
|
110
|
+
* @returns {Promise<Organization>} A promise that resolves to the created Organization object.
|
|
111
|
+
* @throws {Error} Throws an error if the organization creation or profile retrieval fails.
|
|
112
|
+
*/
|
|
113
|
+
async organization(organizationData, options = { getProfile: true }) {
|
|
114
|
+
try {
|
|
115
|
+
const oraganization = new Organization(this._client, organizationData);
|
|
116
|
+
if (options.getProfile) {
|
|
117
|
+
await oraganization.getProfile();
|
|
118
|
+
}
|
|
119
|
+
return oraganization;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error("[Api.organization] Erreur lors de la création d'un objet organisation :", error.message);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a new Project instance and optionally retrieves its profile.
|
|
128
|
+
*
|
|
129
|
+
* @param {Object} projectData - The data used to initialize the Project instance.
|
|
130
|
+
* @param {Object} [options={ getProfile: true }] - Additional options for project creation.
|
|
131
|
+
* @param {boolean} [options.getProfile=true] - Whether to retrieve the project's profile after creation.
|
|
132
|
+
* @returns {Promise<Project>} A promise that resolves to the created Project instance.
|
|
133
|
+
* @throws {Error} If an error occurs during project creation or profile retrieval.
|
|
134
|
+
*/
|
|
135
|
+
async project(projectData, options = { getProfile: true }) {
|
|
136
|
+
try {
|
|
137
|
+
const project = new Project(this._client, projectData);
|
|
138
|
+
if (options.getProfile) {
|
|
139
|
+
await project.getProfile();
|
|
140
|
+
}
|
|
141
|
+
return project;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("[Api.project] Erreur lors de la création d'un objet projet :", error.message);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Retourne l'instance d'ApiClient.
|
|
150
|
+
*
|
|
151
|
+
* @returns {ApiClient}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
get client() {
|
|
155
|
+
return this._client;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
package/src/ApiClient.js
CHANGED
|
@@ -5,6 +5,7 @@ import addFormats from "ajv-formats";
|
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import axiosRetry from "axios-retry";
|
|
7
7
|
import EJSON from "ejson";
|
|
8
|
+
import { jwtDecode } from "jwt-decode";
|
|
8
9
|
import pino from "pino";
|
|
9
10
|
|
|
10
11
|
import MongoID from "./EJSONType.js";
|
|
@@ -146,6 +147,12 @@ export default class ApiClient extends EventEmitter {
|
|
|
146
147
|
setToken(token) {
|
|
147
148
|
this._accessToken = token;
|
|
148
149
|
this._client.defaults.headers.common["Authorization"] = "Bearer " + token;
|
|
150
|
+
// Extrait l'id depuis le token et le stocke si disponible
|
|
151
|
+
const userId = this._getIdFromToken(token);
|
|
152
|
+
if (userId) {
|
|
153
|
+
this._setUserId(userId);
|
|
154
|
+
this._logger.debug(`[ApiClient] userId extrait et défini : ${userId}`);
|
|
155
|
+
}
|
|
149
156
|
this._logger.debug(`[ApiClient] setToken: ${token}`);
|
|
150
157
|
}
|
|
151
158
|
|
|
@@ -165,6 +172,12 @@ export default class ApiClient extends EventEmitter {
|
|
|
165
172
|
*/
|
|
166
173
|
setRefreshToken(rt) {
|
|
167
174
|
this._refreshToken = rt;
|
|
175
|
+
// Vous pouvez faire de même ici si besoin
|
|
176
|
+
const userId = this._getIdFromToken(rt);
|
|
177
|
+
if (userId) {
|
|
178
|
+
this._setUserId(userId);
|
|
179
|
+
this._logger.debug(`[ApiClient] userId extrait depuis refreshToken : ${userId}`);
|
|
180
|
+
}
|
|
168
181
|
this._logger.debug(`[ApiClient] setRefreshToken: ${rt}`);
|
|
169
182
|
}
|
|
170
183
|
|
|
@@ -177,6 +190,36 @@ export default class ApiClient extends EventEmitter {
|
|
|
177
190
|
return this._refreshToken;
|
|
178
191
|
}
|
|
179
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Indique si le client est connecté.
|
|
195
|
+
* On considère que le client est connecté si un token d'accès (_accessToken) est défini.
|
|
196
|
+
*
|
|
197
|
+
* @returns {boolean} True si connecté, false sinon.
|
|
198
|
+
*/
|
|
199
|
+
get isConnected() {
|
|
200
|
+
return !!this._accessToken;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Extrait l'identifiant depuis un JWT.
|
|
205
|
+
*
|
|
206
|
+
* @param {string} token - Le token JWT (accessToken ou refreshToken).
|
|
207
|
+
* @returns {string|null} L'identifiant extrait ou null si non trouvé.
|
|
208
|
+
*/
|
|
209
|
+
_getIdFromToken(token) {
|
|
210
|
+
if (!token) return null;
|
|
211
|
+
try {
|
|
212
|
+
// Décodage du token grâce à jwt-decode
|
|
213
|
+
const payload = jwtDecode(token);
|
|
214
|
+
// L'identifiant peut être dans "id" ou "userId"
|
|
215
|
+
this._logger.debug("[ApiClient] Payload décodé :", payload);
|
|
216
|
+
return payload.id || payload.userId || null;
|
|
217
|
+
} catch (err) {
|
|
218
|
+
this._logger.error("[ApiClient] Erreur lors du décodage du token :", err);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
180
223
|
/**
|
|
181
224
|
* Méthode simplifiée de refresh (en JSON).
|
|
182
225
|
* Emet un event refreshSuccess si ça marche
|
|
@@ -199,6 +242,8 @@ export default class ApiClient extends EventEmitter {
|
|
|
199
242
|
}
|
|
200
243
|
return false;
|
|
201
244
|
} catch (err) {
|
|
245
|
+
// Si on a une erreur, on reset la session
|
|
246
|
+
this.resetSession();
|
|
202
247
|
this._logger.error(`[ApiClient] Refresh Error : ${err.message}`);
|
|
203
248
|
return false;
|
|
204
249
|
}
|
|
@@ -425,6 +470,14 @@ export default class ApiClient extends EventEmitter {
|
|
|
425
470
|
return obj;
|
|
426
471
|
}
|
|
427
472
|
|
|
473
|
+
async safeCall(fn, ...args) {
|
|
474
|
+
try {
|
|
475
|
+
return await fn(...args);
|
|
476
|
+
} catch (error) {
|
|
477
|
+
this._logger.error(`[ApiClient.safeCall] Erreur: ${error.message}`);
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
428
481
|
|
|
429
482
|
/**
|
|
430
483
|
* Calls an API endpoint with the specified parameters.
|
|
@@ -578,9 +631,9 @@ export default class ApiClient extends EventEmitter {
|
|
|
578
631
|
this.setRefreshToken(value);
|
|
579
632
|
break;
|
|
580
633
|
|
|
581
|
-
|
|
582
|
-
this._setUserId(value);
|
|
583
|
-
break;
|
|
634
|
+
// case "setUserId":
|
|
635
|
+
// this._setUserId(value);
|
|
636
|
+
// break;
|
|
584
637
|
|
|
585
638
|
case "resetSession":
|
|
586
639
|
this.resetSession();
|
|
@@ -612,7 +665,12 @@ export default class ApiClient extends EventEmitter {
|
|
|
612
665
|
return response;
|
|
613
666
|
} catch (error) {
|
|
614
667
|
this._updateCircuitBreakerError();
|
|
615
|
-
|
|
668
|
+
this._logger.error(`[ApiClient] Erreur lors de l'appel de ${constant}: ${error.message}`);
|
|
669
|
+
throw new ApiClientError(
|
|
670
|
+
`Erreur lors de l'appel de l'API : ${error.message}`,
|
|
671
|
+
error.response ? error.response.status : 500,
|
|
672
|
+
error.response ? error.response.data : null
|
|
673
|
+
);
|
|
616
674
|
}
|
|
617
675
|
}
|
|
618
676
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ApiResponseError } from "../error.js";
|
|
2
|
+
|
|
3
|
+
// EntityMixin.js
|
|
4
|
+
export const EntityMixin = {
|
|
5
|
+
/**
|
|
6
|
+
* Résout l'identifiant de l'entité si seul le slug est fourni.
|
|
7
|
+
* @param {string} type - Le type d'entité (ex : "citoyens", "organizations", "projects").
|
|
8
|
+
* @returns {Promise<string>} L'identifiant résolu.
|
|
9
|
+
*/
|
|
10
|
+
async resolveId(type) {
|
|
11
|
+
return this.apiClient.safeCall(async () => {
|
|
12
|
+
if (!this.id && this.slug) {
|
|
13
|
+
const response = await this.apiClient.callEndpoint("GET_ELEMENTS_KEY", {
|
|
14
|
+
pathParams:{
|
|
15
|
+
slug: this.slug
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
if(response?.data?.result === true) {
|
|
19
|
+
if(response?.data?.contextId && response?.data?.contextType === type) {
|
|
20
|
+
this.id = response.data.contextId;
|
|
21
|
+
} else {
|
|
22
|
+
throw new ApiResponseError(`Le slug ${this.slug} ne correspond pas à un ${type}`, response.status, response.data);
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
throw new ApiResponseError(`Impossible de récupérer l'identifiant pour le slug ${this.slug}`, response.status, response.data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return this.id;
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Récupère le profil public de l'entité.
|
|
34
|
+
* @returns {Promise<Object>} Les données du profil public.
|
|
35
|
+
*/
|
|
36
|
+
async getPublicProfile() {
|
|
37
|
+
return this.apiClient.safeCall(async () => {
|
|
38
|
+
await this.resolveId(this.getEntityType());
|
|
39
|
+
return this.apiClient.callEndpoint("GET_ELEMENTS_ABOUT", { pathParams: { id: this.id } });
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { EntityMixin } from "./EntityMixin.js";
|
|
2
|
+
|
|
3
|
+
// Organization.js
|
|
4
|
+
export class Organization {
|
|
5
|
+
// Champs privés pour protéger l'état
|
|
6
|
+
#id;
|
|
7
|
+
#slug;
|
|
8
|
+
#data = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Crée une instance de Organization.
|
|
12
|
+
* @param {ApiClient} apiClient - L'instance d'ApiClient.
|
|
13
|
+
* @param {Object} identifier - Objet contenant { id } ou { slug }.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
constructor(apiClient, { id, slug } = {}) {
|
|
17
|
+
if (!id && !slug) {
|
|
18
|
+
throw new Error("Vous devez fournir un id ou un slug pour créer un User.");
|
|
19
|
+
}
|
|
20
|
+
this.apiClient = apiClient;
|
|
21
|
+
this.#id = id || null;
|
|
22
|
+
this.#slug = slug || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Getters en lecture seule pour chaque propriété
|
|
26
|
+
get id() {
|
|
27
|
+
return this.#id;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get slug() {
|
|
31
|
+
return this.#slug;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_slug(newSlug) {
|
|
35
|
+
this.#slug = newSlug;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get isConnected() {
|
|
39
|
+
return this.apiClient.isConnected;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get data() {
|
|
43
|
+
return this.#data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_setData(newData) {
|
|
47
|
+
this.#data = newData;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get userId() {
|
|
51
|
+
return this.apiClient.userId;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get isMe() {
|
|
55
|
+
return this.isConnected && this.userId === this.id;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getEntityType() {
|
|
59
|
+
return "organizations";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Récupère le profil complet de l'organisation.
|
|
64
|
+
*
|
|
65
|
+
* @returns {Promise<Object>} Le profil complet.
|
|
66
|
+
*/
|
|
67
|
+
async getProfile() {
|
|
68
|
+
return this.apiClient.safeCall(async () => {
|
|
69
|
+
const response = await this.getPublicProfile();
|
|
70
|
+
this._setData(response.data);
|
|
71
|
+
return response.data;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Incorporation du mixin dans Organization
|
|
78
|
+
Object.assign(Organization.prototype, EntityMixin);
|
|
79
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { EntityMixin } from "./EntityMixin.js";
|
|
2
|
+
|
|
3
|
+
// Project.js
|
|
4
|
+
export class Project {
|
|
5
|
+
// Champs privés pour protéger l'état
|
|
6
|
+
#id;
|
|
7
|
+
#slug;
|
|
8
|
+
#data = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Crée une instance de Project.
|
|
12
|
+
* @param {ApiClient} apiClient - L'instance d'ApiClient.
|
|
13
|
+
* @param {Object} identifier - Objet contenant { id } ou { slug }.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
constructor(apiClient, { id, slug } = {}) {
|
|
17
|
+
if (!id && !slug) {
|
|
18
|
+
throw new Error("Vous devez fournir un id ou un slug pour créer un User.");
|
|
19
|
+
}
|
|
20
|
+
this.apiClient = apiClient;
|
|
21
|
+
this.#id = id || null;
|
|
22
|
+
this.#slug = slug || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Getters en lecture seule pour chaque propriété
|
|
26
|
+
get id() {
|
|
27
|
+
return this.#id;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get slug() {
|
|
31
|
+
return this.#slug;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_slug(newSlug) {
|
|
35
|
+
this.#slug = newSlug;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get isConnected() {
|
|
39
|
+
return this.apiClient.isConnected;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get data() {
|
|
43
|
+
return this.#data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_setData(newData) {
|
|
47
|
+
this.#data = newData;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get userId() {
|
|
51
|
+
return this.apiClient.userId;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get isMe() {
|
|
55
|
+
return this.isConnected && this.userId === this.id;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getEntityType() {
|
|
59
|
+
return "projects";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Récupère le profil complet de l'organisation.
|
|
64
|
+
*
|
|
65
|
+
* @returns {Promise<Object>} Le profil complet.
|
|
66
|
+
*/
|
|
67
|
+
async getProfile() {
|
|
68
|
+
return this.apiClient.safeCall(async () => {
|
|
69
|
+
const response = await this.getPublicProfile();
|
|
70
|
+
this._setData(response.data);
|
|
71
|
+
return response.data;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Incorporation du mixin dans Project
|
|
78
|
+
Object.assign(Project.prototype, EntityMixin);
|
|
79
|
+
|
package/src/api/User.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { EntityMixin } from "./EntityMixin.js";
|
|
2
|
+
|
|
3
|
+
// User.js
|
|
4
|
+
export class User {
|
|
5
|
+
// Champs privés pour protéger l'état
|
|
6
|
+
#id;
|
|
7
|
+
#slug;
|
|
8
|
+
#data;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Crée une instance de User.
|
|
12
|
+
* @param {ApiClient} apiClient - L'instance d'ApiClient.
|
|
13
|
+
* @param {Object} identifier - Objet contenant { id } ou { slug }.
|
|
14
|
+
* @param {Object} [data={}] - Données supplémentaires.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
constructor(apiClient, { id, slug } = {}, data = {}) {
|
|
18
|
+
if (!id && !slug) {
|
|
19
|
+
throw new Error("Vous devez fournir un id ou un slug pour créer un User.");
|
|
20
|
+
}
|
|
21
|
+
this.apiClient = apiClient;
|
|
22
|
+
this.#id = id || null;
|
|
23
|
+
this.#slug = slug || null;
|
|
24
|
+
this.#data = data;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Getters en lecture seule pour chaque propriété
|
|
28
|
+
get id() {
|
|
29
|
+
return this.#id;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get slug() {
|
|
33
|
+
return this.#slug;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_slug(newSlug) {
|
|
37
|
+
this.#slug = newSlug;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get isConnected() {
|
|
41
|
+
return this.apiClient.isConnected;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Getter pour accéder aux données
|
|
45
|
+
get data() {
|
|
46
|
+
return this.#data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Méthode interne qui permet de mettre à jour les données
|
|
50
|
+
_setData(newData) {
|
|
51
|
+
this.#data = newData;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getEntityType() {
|
|
55
|
+
return "citoyens";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get userId() {
|
|
59
|
+
return this.apiClient.userId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get isMe() {
|
|
63
|
+
return this.isConnected && this.userId === this.id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Récupère le profil complet de l'utilisateur.
|
|
68
|
+
* Si l'utilisateur est connecté, on appelle le endpoint ME_INFO_URL,
|
|
69
|
+
* sinon, on peut imaginer appeler un endpoint public.
|
|
70
|
+
*
|
|
71
|
+
* @returns {Promise<Object>} Le profil complet.
|
|
72
|
+
*/
|
|
73
|
+
async getProfile() {
|
|
74
|
+
return this.apiClient.safeCall(async () => {
|
|
75
|
+
if (this.isMe) {
|
|
76
|
+
const response = await this.apiClient.callEndpoint("ME_INFO_URL", {});
|
|
77
|
+
return response.data;
|
|
78
|
+
} else {
|
|
79
|
+
const response = await this.getPublicProfile();
|
|
80
|
+
return response.data;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Incorporation du mixin dans User
|
|
88
|
+
Object.assign(User.prototype, EntityMixin);
|
|
89
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// UserApi.js
|
|
2
|
+
import ApiClient from "../ApiClient.js";
|
|
3
|
+
import { ApiResponseError } from "../error.js";
|
|
4
|
+
import { User } from "./User.js";
|
|
5
|
+
|
|
6
|
+
export class UserApi {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
// Injection de dépendance : ApiClient est créé à partir des options
|
|
9
|
+
this.client = new ApiClient(options);
|
|
10
|
+
this.loggedUser = null;
|
|
11
|
+
}
|
|
12
|
+
// Méthode d'authentification : récupère les données utilisateur depuis un endpoint
|
|
13
|
+
async login(email, password) {
|
|
14
|
+
return this.client.safeCall(async () => {
|
|
15
|
+
// Appel à un endpoint d'authentification
|
|
16
|
+
const response = await this.client.callEndpoint("AUTHENTICATE_URL", { email, password });
|
|
17
|
+
// Création d'une instance de LoggedInUser à partir des données reçues
|
|
18
|
+
this.loggedUser = new User(this.client, { id: response.data.user.id }, response.data.user);
|
|
19
|
+
return this.loggedUser;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async register({
|
|
24
|
+
name,
|
|
25
|
+
username,
|
|
26
|
+
email,
|
|
27
|
+
pwd,
|
|
28
|
+
} = {}) {
|
|
29
|
+
return this.client.safeCall(async () => {
|
|
30
|
+
const response = await this.client.callEndpoint("PERSON_REGISTER", { name, username, email, pwd });
|
|
31
|
+
if(response?.data?.result === false) {
|
|
32
|
+
throw new ApiResponseError(response.data.msg, response.status, response.data);
|
|
33
|
+
}
|
|
34
|
+
return response.data;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async recoverPassword(email) {
|
|
40
|
+
return this.client.safeCall(async () => {
|
|
41
|
+
const response = await this.client.callEndpoint("PASSWORD_RECOVERY", { email });
|
|
42
|
+
if(response?.data?.result === false) {
|
|
43
|
+
throw new ApiResponseError(response.data.msg, response.status, response.data);
|
|
44
|
+
}
|
|
45
|
+
return response.data;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|