@communecter/cocolight-api-client 1.0.6 → 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 +2 -2
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/package.json +2 -1
- package/src/Api.js +158 -0
- package/src/ApiClient.js +28 -3
- 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/src/ApiClient.js
CHANGED
|
@@ -190,6 +190,16 @@ export default class ApiClient extends EventEmitter {
|
|
|
190
190
|
return this._refreshToken;
|
|
191
191
|
}
|
|
192
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
|
+
|
|
193
203
|
/**
|
|
194
204
|
* Extrait l'identifiant depuis un JWT.
|
|
195
205
|
*
|
|
@@ -202,10 +212,10 @@ export default class ApiClient extends EventEmitter {
|
|
|
202
212
|
// Décodage du token grâce à jwt-decode
|
|
203
213
|
const payload = jwtDecode(token);
|
|
204
214
|
// L'identifiant peut être dans "id" ou "userId"
|
|
205
|
-
this._logger.
|
|
215
|
+
this._logger.debug("[ApiClient] Payload décodé :", payload);
|
|
206
216
|
return payload.id || payload.userId || null;
|
|
207
217
|
} catch (err) {
|
|
208
|
-
this._logger.error("[ApiClient] Erreur lors du décodage du token :", err
|
|
218
|
+
this._logger.error("[ApiClient] Erreur lors du décodage du token :", err);
|
|
209
219
|
return null;
|
|
210
220
|
}
|
|
211
221
|
}
|
|
@@ -232,6 +242,8 @@ export default class ApiClient extends EventEmitter {
|
|
|
232
242
|
}
|
|
233
243
|
return false;
|
|
234
244
|
} catch (err) {
|
|
245
|
+
// Si on a une erreur, on reset la session
|
|
246
|
+
this.resetSession();
|
|
235
247
|
this._logger.error(`[ApiClient] Refresh Error : ${err.message}`);
|
|
236
248
|
return false;
|
|
237
249
|
}
|
|
@@ -458,6 +470,14 @@ export default class ApiClient extends EventEmitter {
|
|
|
458
470
|
return obj;
|
|
459
471
|
}
|
|
460
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
|
+
}
|
|
461
481
|
|
|
462
482
|
/**
|
|
463
483
|
* Calls an API endpoint with the specified parameters.
|
|
@@ -645,7 +665,12 @@ export default class ApiClient extends EventEmitter {
|
|
|
645
665
|
return response;
|
|
646
666
|
} catch (error) {
|
|
647
667
|
this._updateCircuitBreakerError();
|
|
648
|
-
|
|
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
|
+
);
|
|
649
674
|
}
|
|
650
675
|
}
|
|
651
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
|
+
}
|