@communecter/cocolight-api-client 1.0.22 → 1.0.23

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.
@@ -138,25 +138,55 @@ export class Organization extends BaseEntity {
138
138
  * Constant : GET_MEMBERS_ADMIN / GET_MEMBERS_NO_ADMIN
139
139
  * @param {Object} data - Les données de requête.
140
140
  * @param {Object} options - Options supplémentaires.
141
+ * @param {boolean} options.toBeValidated - Indique si les membres doivent être validés.
141
142
  * @param {boolean} options.isAdmin - Indique si l'utilisateur est admin.
143
+ * @param {boolean} options.isAdminPending - Indique si l'utilisateur est en attente de validation pour être admin.
142
144
  * @param {boolean} options.isInviting - Indique si l'utilisateur est en attente d'invitation.
143
145
  * @param {Array} options.roles - Liste des rôles à filtrer.
144
146
  * @returns {Promise<Object>} - Un objet contenant le nombre de membres et la liste des membres.
147
+ * @throws {ApiResponseError} - Si une erreur se produit lors de la récupération des contributeurs.
148
+ *
149
+ * @example
150
+ * // Récupérer tous les membres
151
+ * const members = await organization.getMembers();
152
+ *
153
+ * // Récupérer les membres avec validation en attente
154
+ * const membersToBeValidated = await organization.getMembers({}, { toBeValidated: true });
155
+ *
156
+ * // Récupérer les membres avec un rôle spécifique
157
+ * const membersWithRole = await organization.getMembers({}, { roles: ['admin'] });
158
+ *
159
+ * // Récupérer les membres en attente d'invitation
160
+ * const invitingMembers = await organization.getMembers({}, { isInviting: true });
161
+ *
162
+ * // Récupérer les membres admin
163
+ * const adminMembers = await organization.getMembers({}, { isAdmin: true });
164
+ *
165
+ * // Récupérer les membres admin et en attente d'invitation
166
+ * const adminInvitingMembers = await organization.getMembers({}, { isAdmin: true, isInviting: true });
167
+ *
168
+ * // Récupérer les membres avec validation en attente et admin
169
+ * const adminToBeValidatedMembers = await organization.getMembers({}, { toBeValidated: true, isAdmin: true });
170
+ *
171
+ * // Récupérer les membres en attente de validation pour être admin
172
+ * const adminPendingMembers = await organization.getMembers({}, { isAdminPending: true });
173
+ *
174
+ *
145
175
  */
146
176
  async getMembers(data = {}, options = {}) {
147
- const { isAdmin, isInviting, roles = [] } = options;
177
+ const { toBeValidated, isAdmin, isAdminPending, isInviting, roles = [] } = options;
148
178
 
149
179
  if(this.isMe){
150
180
  data.pathParams = { type: this.getEntityType(), id: this.id };
151
- // NOTE : dans le schema je crois que si pas de data.filters alors le default ce fait avec data.pathParams
152
181
  // data.filters = {
153
182
  // [`links.memberOf.${this.id}`]: { "$exists": true },
154
183
  // [`links.memberOf.${this.id}.toBeValidated`]: { "$exists": false },
155
184
  // [`links.memberOf.${this.id}.isInviting`]: { "$exists": false }
156
185
  // };
186
+ data.filters = this._buildLinkFilters(this.id, { linkType: "memberOf", toBeValidated, isAdmin, isAdminPending, isInviting, roles });
157
187
  } else {
158
188
  delete data?.pathParams;
159
- data.filters = this._buildMemberFilters(this.id, { isAdmin, isInviting, roles });
189
+ data.filters = this._buildLinkFilters(this.id, { linkType: "memberOf", toBeValidated: "false", isAdmin, isInviting, roles });
160
190
  }
161
191
 
162
192
  const fetchFn = this.isMe
@@ -172,43 +202,11 @@ export class Organization extends BaseEntity {
172
202
  const rawList = this._linkEntities(arrayObjet.results);
173
203
 
174
204
  return {
175
- count: arrayObjet.count?.poi ?? 0,
205
+ count: arrayObjet.count,
176
206
  results: rawList
177
207
  };
178
208
  }
179
209
 
180
- _buildMemberFilters(id, { isAdmin, isInviting, roles }) {
181
-
182
- if (typeof isAdmin !== "undefined" && typeof isAdmin !== "boolean") {
183
- throw new TypeError("isAdmin doit être un booléen.");
184
- }
185
- if (typeof isInviting !== "undefined" && typeof isInviting !== "boolean") {
186
- throw new TypeError("isInviting doit être un booléen.");
187
- }
188
- if (!Array.isArray(roles)) {
189
- throw new TypeError("roles doit être un tableau de chaînes.");
190
- }
191
-
192
- const filters = {
193
- [`links.memberOf.${id}`]: { $exists: true },
194
- [`links.memberOf.${id}.toBeValidated`]: { $exists: false }
195
- };
196
-
197
- if (isInviting === true || isInviting === false) {
198
- filters[`links.memberOf.${id}.isInviting`] = { $exists: isInviting };
199
- }
200
-
201
- if (isAdmin === true) {
202
- filters[`links.memberOf.${id}.isAdmin`] = { $exists: true };
203
- }
204
-
205
- if (Array.isArray(roles) && roles.length > 0) {
206
- filters[`links.memberOf.${id}.roles`] = { $in: roles };
207
- }
208
-
209
- return filters;
210
- }
211
-
212
210
  /**
213
211
  * Crée une instance de projet et récupère son profil si nécessaire.
214
212
  *
@@ -267,5 +265,78 @@ export class Organization extends BaseEntity {
267
265
  async news(newsData = {}) {
268
266
  return super.news(newsData);
269
267
  }
268
+
269
+ /**
270
+ * ───────────────────────────────
271
+ * Lien utilisateur ↔ organisation
272
+ * (rejoindre, valider, quitter, devenir admin)
273
+ * ───────────────────────────────
274
+ */
275
+
276
+
277
+ /**
278
+ * Envoie une demande pour rejoindre l'organisation en tant que membre.
279
+ * L'action est soumise à validation par un administrateur de l'organisation.
280
+ *
281
+ * @returns {Promise<Object>} - Résultat de la requête d'adhésion.
282
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité ne supporte pas l'action.
283
+ */
284
+ async requestToJoin(){
285
+ return super.requestToJoin();
286
+ }
287
+
288
+ /**
289
+ * Envoie une demande pour rejoindre l'organisation en tant qu'administrateur.
290
+ * L'action est soumise à validation par un administrateur existant.
291
+ *
292
+ * @returns {Promise<Object>} - Résultat de la requête d'administration.
293
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité ne supporte pas l'action.
294
+ */
295
+ async requestToJoinAdmin(){
296
+ return super.requestToJoinAdmin();
297
+ }
298
+
299
+ /**
300
+ * Accepte une invitation à rejoindre l'organisation.
301
+ * Cette action valide un lien en attente avec l'option `isInviting`.
302
+ *
303
+ * @returns {Promise<Object>} - Résultat de la validation du lien.
304
+ * @throws {ApiError} - Si aucune invitation n'est en attente ou si l'entité ne supporte pas l'action.
305
+ */
306
+ async acceptInvitation(){
307
+ return super.acceptInvitation();
308
+ }
309
+
310
+ /**
311
+ * Quitte l'organisation, que ce soit en tant que membre ou administrateur.
312
+ * Cette action supprime le lien entre l'utilisateur et l'organisation.
313
+ *
314
+ * @returns {Promise<Object>} - Résultat de la déconnexion.
315
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou non membre.
316
+ */
317
+ async leave() {
318
+ return super.leave();
319
+ }
320
+
321
+ /**
322
+ * Suivre une organisation.
323
+ * Cette action permet à l'utilisateur de suivre l'organisation.
324
+ *
325
+ * @returns {Promise<Object>} - Résultat de l'action de suivi.
326
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité ne supporte pas l'action.
327
+ */
328
+ async follow() {
329
+ return super.follow();
330
+ }
331
+
332
+ /**
333
+ * Se désabonne d'une organisation.
334
+ *
335
+ * @returns {Promise<Object>} - Résultat de la désinscription.
336
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée.
337
+ */
338
+ async unfollow() {
339
+ return super.unfollow();
340
+ }
270
341
 
271
342
  }
package/src/api/Poi.js CHANGED
@@ -154,5 +154,27 @@ export class Poi extends BaseEntity {
154
154
  async news(newsData = {}) {
155
155
  return super.news(newsData);
156
156
  }
157
+
158
+ /**
159
+ * Suivre un POI.
160
+ * Cette action permet à l'utilisateur de suivre un POI.
161
+ *
162
+ * @returns {Promise<Object>} - Résultat de l'action de suivi.
163
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité ne supporte pas l'action.
164
+ */
165
+ async follow() {
166
+ return super.follow();
167
+ }
168
+
169
+ /**
170
+ * Se désabonne d'un POI.
171
+ *
172
+ * @returns {Promise<Object>} - Résultat de la désinscription.
173
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée.
174
+ */
175
+ async unfollow() {
176
+ return super.unfollow();
177
+ }
178
+
157
179
 
158
180
  }
@@ -1,3 +1,4 @@
1
+ import { ApiResponseError } from "../error.js";
1
2
  import BaseEntity from "./BaseEntity.js";
2
3
 
3
4
  export class Project extends BaseEntity {
@@ -153,6 +154,70 @@ export class Project extends BaseEntity {
153
154
  return super.getSubscribers(data);
154
155
  }
155
156
 
157
+ /**
158
+ * Récupérer les contributeurs d'un projet.
159
+ * Constant : GET_CONTRIBUTORS_ADMIN / GET_CONTRIBUTORS_NO_ADMIN
160
+ * @param {Object} data - Les données de requête.
161
+ * @param {Object} options - Options supplémentaires.
162
+ * @param {boolean} options.toBeValidated - Indique si les contributeurs doivent être validés.
163
+ * @param {boolean} options.isAdmin - Indique si l'utilisateur est admin.
164
+ * @param {boolean} options.isInviting - Indique si l'utilisateur est en attente d'invitation.
165
+ * @param {Array} options.roles - Liste des rôles à filtrer.
166
+ * @returns {Promise<Object>} - Un objet contenant le nombre de contributeurs et la liste des contributeurs.
167
+ * @throws {ApiResponseError} - Si une erreur se produit lors de la récupération des contributeurs.
168
+ *
169
+ * @example
170
+ * // Récupérer tous les contributeurs
171
+ * const contributors = await project.getContributors();
172
+ *
173
+ * // Récupérer les contributeurs avec validation en attente
174
+ * const contributorsToBeValidated = await project.getContributors({}, { toBeValidated: true });
175
+ *
176
+ * // Récupérer les contributeurs avec un rôle spécifique
177
+ * const contributorsWithRole = await project.getContributors({}, { roles: ["admin"] });
178
+ *
179
+ * // Récupérer les contributeurs administrateurs
180
+ * const adminContributors = await project.getContributors({}, { isAdmin: true });
181
+ *
182
+ * // Récupérer les contributeurs en attente d'invitation
183
+ * const invitingContributors = await project.getContributors({}, { isInviting: true });
184
+ *
185
+ */
186
+ async getContributors(data = {}, options = {}) {
187
+ const { toBeValidated, isAdmin, isInviting, isAdminPending, roles = [] } = options;
188
+
189
+ if(this.isMe){
190
+ data.pathParams = { type: this.getEntityType(), id: this.id };
191
+ // NOTE : dans le schema je crois que si pas de data.filters alors le default ce fait avec data.pathParams
192
+ // data.filters = {
193
+ // [`links.projects.${this.id}`]: { "$exists": true },
194
+ // [`links.projects.${this.id}.toBeValidated`]: { "$exists": false },
195
+ // [`links.projects.${this.id}.isInviting`]: { "$exists": false }
196
+ // };
197
+ data.filters = this._buildLinkFilters(this.id, { linkType: "projects", toBeValidated, isAdmin, isAdminPending, isInviting, roles });
198
+ } else {
199
+ delete data?.pathParams;
200
+ data.filters = this._buildLinkFilters(this.id, { linkType: "projects", toBeValidated: "false", isAdmin, isInviting, roles });
201
+ }
202
+
203
+ const fetchFn = this.isMe
204
+ ? () => this.callIsMe(() => this.endpointApi.getContributorsAdmin(data))
205
+ : () => this.endpointApi.getContributorsNoAdmin(data);
206
+
207
+ const arrayObjet = await fetchFn();
208
+ if (!Array.isArray(arrayObjet.results)) {
209
+ throw new ApiResponseError("Erreur lors de la récupération des contributeurs", 500, arrayObjet.results);
210
+ }
211
+
212
+ // lier les entités au objets
213
+ const rawList = this._linkEntities(arrayObjet.results);
214
+
215
+ return {
216
+ count: arrayObjet.count,
217
+ results: rawList
218
+ };
219
+ }
220
+
156
221
  /**
157
222
  * Crée une instance de projet et récupère son profil si nécessaire.
158
223
  *
@@ -212,4 +277,76 @@ export class Project extends BaseEntity {
212
277
  return super.news(newsData);
213
278
  }
214
279
 
280
+ /**
281
+ * ───────────────────────────────
282
+ * Lien utilisateur ↔ projet
283
+ * (rejoindre, valider, quitter, devenir admin)
284
+ * ───────────────────────────────
285
+ */
286
+
287
+ /**
288
+ * Envoie une demande pour rejoindre le projet en tant que contributeur.
289
+ * L'action est soumise à validation par un administrateur du projet.
290
+ *
291
+ * @returns {Promise<Object>} - Résultat de la requête d'adhésion.
292
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si le projet ne supporte pas l'action.
293
+ */
294
+ async requestToJoin(){
295
+ return super.requestToJoin();
296
+ }
297
+
298
+ /**
299
+ * Envoie une demande pour devenir administrateur du projet.
300
+ * L'action est soumise à validation par un administrateur existant.
301
+ *
302
+ * @returns {Promise<Object>} - Résultat de la requête d'administration.
303
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si le projet ne supporte pas l'action.
304
+ */
305
+ async requestToJoinAdmin(){
306
+ return super.requestToJoinAdmin();
307
+ }
308
+
309
+ /**
310
+ * Accepte une invitation à rejoindre le projet.
311
+ * Cette action valide un lien en attente avec l'option `isInviting`.
312
+ *
313
+ * @returns {Promise<Object>} - Résultat de la validation du lien.
314
+ * @throws {ApiError} - Si aucune invitation n'est en attente ou si le projet ne supporte pas l'action.
315
+ */
316
+ async acceptInvitation(){
317
+ return super.acceptInvitation();
318
+ }
319
+
320
+ /**
321
+ * Quitte le projet, que ce soit en tant que contributeur ou administrateur.
322
+ * Cette action supprime le lien entre l'utilisateur et le projet.
323
+ *
324
+ * @returns {Promise<Object>} - Résultat de la déconnexion.
325
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou non contributeur.
326
+ */
327
+ async leave() {
328
+ return super.leave();
329
+ }
330
+
331
+ /**
332
+ * Suivre un projet.
333
+ * Cette action permet à l'utilisateur de suivre le projet.
334
+ *
335
+ * @returns {Promise<Object>} - Résultat de l'action de suivi.
336
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité ne supporte pas l'action.
337
+ */
338
+ async follow() {
339
+ return super.follow();
340
+ }
341
+
342
+ /**
343
+ * Se désabonne d'un projet.
344
+ *
345
+ * @returns {Promise<Object>} - Résultat de la désinscription.
346
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée.
347
+ */
348
+ async unfollow() {
349
+ return super.unfollow();
350
+ }
351
+
215
352
  }
package/src/api/User.js CHANGED
@@ -81,7 +81,7 @@ export class User extends BaseEntity {
81
81
 
82
82
  super(parent, data, deps);
83
83
  }
84
-
84
+
85
85
  get slug() {
86
86
  return this._draftData.slug || null;
87
87
  }
@@ -90,6 +90,14 @@ export class User extends BaseEntity {
90
90
  return this.isConnected && this.userId === this.id;
91
91
  }
92
92
 
93
+ get parentIsMe() {
94
+ return super.isMe;
95
+ }
96
+
97
+ get isActingUser() {
98
+ return this.parentIsMe && !this.isMe;
99
+ }
100
+
93
101
  /**
94
102
  * Récupère le profil complet de l'utilisateur.
95
103
  * Si l'utilisateur est connecté, on appelle le endpoint ME_INFO_URL,
@@ -181,23 +189,7 @@ export class User extends BaseEntity {
181
189
  }
182
190
 
183
191
  static fromServerData(data, parent, deps) {
184
- const instance = new User(parent.apiClient, data, deps);
185
- instance._serverData = { ...data };
186
- // est ce que je besoin de ça si il est contruit dans le constructeur ?
187
- // il doit y avaoir une raison pour les autres objets
188
- const { draft, proxy } = instance._buildDraftAndProxy({
189
- data: { ...data, ...instance.defaultFields },
190
- serverData: instance._serverData,
191
- constant: User.SCHEMA_CONSTANTS,
192
- apiClient: instance.apiClient,
193
- transforms: instance.transforms,
194
- removeFields: instance.removeFields
195
- });
196
-
197
- instance._draftData = draft;
198
- instance.data = proxy;
199
-
200
- return instance;
192
+ return new User(parent.apiClient, data, deps);
201
193
  }
202
194
 
203
195
  /**
@@ -451,6 +443,18 @@ export class User extends BaseEntity {
451
443
  return filteredBadges;
452
444
  }
453
445
 
446
+ async user(userData) {
447
+ if(!this.isMe){
448
+ throw new ApiError("Vous devez être connecté et être l'utilisateur");
449
+ }
450
+ if (!userData.id && !userData.slug) {
451
+ throw new ApiError("Vous devez fournir un id ou un slug pour créer un User.");
452
+ }
453
+ const user = new User(this, userData, this.deps);
454
+ await user.get();
455
+ return user;
456
+ }
457
+
454
458
  /**
455
459
  * Crée une instance d'organisation et récupère son profil si nécessaire.
456
460
  *
@@ -535,6 +539,209 @@ export class User extends BaseEntity {
535
539
  return super.badge(badgeData);
536
540
  }
537
541
 
542
+ /**
543
+ * ───────────────────────────────
544
+ * Lien utilisateur ↔ utilisateur (ami)
545
+ * (demander, valider, se retirer)
546
+ * ───────────────────────────────
547
+ */
548
+
549
+ /**
550
+ * Envoie une demande d'amitié à cet utilisateur.
551
+ * L'utilisateur ciblé devra valider la demande pour établir la relation.
552
+ *
553
+ * @returns {Promise<Object>} - Résultat de la requête.
554
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
555
+ */
556
+ async sendFriendRequest() {
557
+ if (!this.isActingUser) {
558
+ throw new ApiError("Vous devez être connecté pour envoyer une demande d'amis.");
559
+ }
560
+
561
+ this._checkLinkableEntity();
562
+ if (!this.id) {
563
+ throw new ApiError(`${this.constructor.name} non enregistrée.`);
564
+ }
565
+
566
+ const { connectTypeConnect } = this._getLinkMeta();
567
+ const userLink = this._getLinkFromConnectedUser();
568
+
569
+ if (!userLink) {
570
+ const data = {
571
+ parentType: this.getEntityType(),
572
+ parentId: this.id,
573
+ connectType: connectTypeConnect
574
+ };
575
+ const retour = await this.endpointApi.connect(data);
576
+ // TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
577
+ await this.parent.refresh();
578
+ return retour;
579
+ }
580
+
581
+ if (userLink.isInviting && userLink.invitorId === this.id) {
582
+ return this.acceptFriendRequest();
583
+ }
584
+
585
+ if (userLink.isInviting && userLink.invitorId === this.userId) {
586
+ throw new ApiError("Vous avez déjà envoyé une demande d'amis à cet utilisateur.");
587
+ }
588
+
589
+ throw new ApiError("Vous êtes déjà connecté à cette entité.");
590
+ }
591
+
592
+ /**
593
+ * Accepte une demande d'amitié envoyée par cet utilisateur.
594
+ * Cette action établit un lien entre les deux utilisateurs.
595
+ *
596
+ * @returns {Promise<Object>} - Résultat de la validation du lien.
597
+ * @throws {ApiError} - Si aucune invitation n'est en attente ou si l'action est interdite.
598
+ */
599
+ async acceptFriendRequest() {
600
+ if (!this.isActingUser) {
601
+ throw new ApiError("Vous devez être connecté pour accepter une demande d'amitié.");
602
+ }
603
+
604
+ this._checkLinkableEntity();
605
+ if (!this.id) {
606
+ throw new ApiError(`${this.constructor.name} non enregistrée.`);
607
+ }
608
+
609
+ const userLink = this._getLinkFromConnectedUser();
610
+
611
+ if (userLink?.isInviting && userLink.invitorId === this.id) {
612
+ const data = {
613
+ parentType: this.getEntityType(),
614
+ parentId: this.id,
615
+ linkOption: "isInviting"
616
+ };
617
+ const retour = await this.endpointApi.linkValidate(data);
618
+ // TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
619
+ await this.parent.refresh();
620
+ return retour;
621
+ }
622
+
623
+ throw new ApiError("Vous n'avez pas d'invitation à valider.");
624
+ }
625
+
626
+ /**
627
+ * Supprime la relation d'amitié avec cet utilisateur.
628
+ * Cette action annule tout lien existant entre les deux profils.
629
+ *
630
+ * @returns {Promise<Object>} - Résultat de la suppression.
631
+ * @throws {ApiError} - Si aucune relation n'existe.
632
+ */
633
+ async removeFriend() {
634
+ if (!this.isActingUser) {
635
+ throw new ApiError("Vous devez être connecté pour supprimer un ami.");
636
+ }
637
+
638
+ this._checkLinkableEntity();
639
+ if (!this.id) {
640
+ throw new ApiError(`${this.constructor.name} non enregistrée.`);
641
+ }
642
+
643
+ const { connectTypeDisconnect } = this._getLinkMeta();
644
+ const userLink = this._getLinkFromConnectedUser();
645
+
646
+ if (!userLink) {
647
+ throw new ApiError("Vous n'êtes pas connecté à cette entité.");
648
+ }
649
+
650
+ const data = {
651
+ parentType: this.getEntityType(),
652
+ parentId: this.id,
653
+ connectType: connectTypeDisconnect
654
+ };
655
+ const retour = await this.endpointApi.disconnect(data);
656
+ // TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
657
+ await this.parent.refresh();
658
+ return retour;
659
+ }
660
+
661
+
662
+ requestToJoin() {
663
+ throw new ApiError("l'utilisation de requestToJoin n'est pas autorisée sur un utilisateur.");
664
+ }
665
+
666
+ requestToJoinAdmin() {
667
+ throw new ApiError("l'utilisation de requestToJoinAdmin n'est pas autorisée sur un utilisateur.");
668
+ }
669
+
670
+ acceptInvitation() {
671
+ throw new ApiError("l'utilisation de acceptInvitation n'est pas autorisée sur un utilisateur.");
672
+ }
673
+
674
+ leave() {
675
+ throw new ApiError("l'utilisation de leave n'est pas autorisée sur un utilisateur.");
676
+ }
677
+
678
+ /**
679
+ * Suivre un utilisateur
680
+ * Cette action permet à l'utilisateur connecté de suivre un autre utilisateur.
681
+ * Elle nécessite que l'utilisateur soit connecté.
682
+ *
683
+ * @returns {Promise<Object>} - Résultat de la requête.
684
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
685
+ */
686
+ async follow() {
687
+ if (!this.isActingUser) {
688
+ throw new ApiError("Vous devez être connecté pour suivre un utilisateur.");
689
+ }
690
+ if (!this.id) {
691
+ throw new ApiError(`${this.constructor.name} non enregistrée.`);
692
+ }
693
+
694
+ const userLink = this.parent?.serverData?.links?.["follows"]?.[this.id] || null;
695
+
696
+ if (!userLink) {
697
+ const data = {
698
+ parentType: this.getEntityType(),
699
+ parentId: this.id
700
+ };
701
+ const retour = await this.endpointApi.follow(data);
702
+ // TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
703
+ await this.parent.refresh();
704
+ return retour;
705
+ }
706
+
707
+ throw new ApiError("Vous êtes déjà abonné à cet utilisateur.");
708
+ }
709
+
710
+ /**
711
+ * Se désabonner d'un utilisateur
712
+ * Cette action permet à l'utilisateur connecté de se désabonner d'un autre utilisateur.
713
+ * Elle nécessite que l'utilisateur soit connecté.
714
+ *
715
+ * @returns {Promise<Object>} - Résultat de la requête.
716
+ * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
717
+ */
718
+ async unfollow() {
719
+ if (!this.isActingUser) {
720
+ throw new ApiError("Vous devez être connecté pour vous désabonner d'un utilisateur.");
721
+ }
722
+ if (!this.id) {
723
+ throw new ApiError(`${this.constructor.name} non enregistrée.`);
724
+ }
725
+
726
+ const userLink = this.parent?.serverData?.links?.["follows"]?.[this.id] || null;
727
+
728
+ if (userLink) {
729
+ const data = {
730
+ parentType: this.getEntityType(),
731
+ parentId: this.id,
732
+ connectType: "followers"
733
+ };
734
+ const retour = await this.endpointApi.disconnect(data);
735
+ // TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
736
+ await this.parent.refresh();
737
+ return retour;
738
+ }
739
+
740
+ throw new ApiError("Vous n'êtes pas abonné à cet utilisateur.");
741
+ }
742
+
743
+
744
+
538
745
  }
539
746
 
540
747
  // Incorporation des mixins dans User