@communecter/cocolight-api-client 1.0.21 → 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.
- 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 +1 -1
- package/src/api/BaseEntity.js +460 -18
- package/src/api/EndpointApi.js +110 -105
- package/src/api/EndpointApi.types.d.ts +23 -2
- package/src/api/Event.js +22 -0
- package/src/api/Organization.js +107 -36
- package/src/api/Poi.js +22 -0
- package/src/api/Project.js +137 -0
- package/src/api/User.js +225 -18
- package/src/endpoints.module.js +2 -2
- package/src/index.js +21 -1
- package/src/utils/reactive.js +279 -0
package/package.json
CHANGED
package/src/api/BaseEntity.js
CHANGED
|
@@ -4,8 +4,13 @@ import ObjectID from "bson-objectid";
|
|
|
4
4
|
import pkg from "file-type";
|
|
5
5
|
|
|
6
6
|
import { ApiAuthenticationError, ApiError, ApiResponseError, ApiValidationError } from "../error.js";
|
|
7
|
+
import { autoSyncDraftFromSchema, reactive } from "../utils/reactive.js";
|
|
7
8
|
const { fromBuffer } = pkg;
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import("../ApiClient.js").default} ApiClient
|
|
12
|
+
* @typedef {import("./EndpointApi.js").default} EndpointApi
|
|
13
|
+
*/
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Classe de base pour toutes les entités métiers : utilisateurs, projets, organisations, etc.
|
|
@@ -26,6 +31,9 @@ export class BaseEntity {
|
|
|
26
31
|
/** @type {boolean} Indique si `save()` est en cours */
|
|
27
32
|
_calledFromSave = false;
|
|
28
33
|
|
|
34
|
+
/** @type {boolean} Indique si le draft est synchronisé avec le serveur */
|
|
35
|
+
_syncReactiveDraft = false;
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* Constructeur de l'entité.
|
|
31
39
|
* @param {Object} parent - L'ApiClient ou une entité parente.
|
|
@@ -50,11 +58,11 @@ export class BaseEntity {
|
|
|
50
58
|
this.deps = deps;
|
|
51
59
|
|
|
52
60
|
if (parent?.__entityTag === "ApiClient") {
|
|
53
|
-
/** @type {
|
|
61
|
+
/** @type {ApiClient} */
|
|
54
62
|
this.apiClient = parent;
|
|
55
63
|
this.parent = null;
|
|
56
64
|
} else if (parent?.apiClient) {
|
|
57
|
-
/** @type {
|
|
65
|
+
/** @type {ApiClient} */
|
|
58
66
|
this.apiClient = parent.apiClient;
|
|
59
67
|
this.parent = parent;
|
|
60
68
|
} else {
|
|
@@ -63,10 +71,10 @@ export class BaseEntity {
|
|
|
63
71
|
|
|
64
72
|
// Gérer les deux cas : fonction constructeur ou instance
|
|
65
73
|
if (typeof deps.EndpointApi === "function") {
|
|
66
|
-
/** @type {
|
|
74
|
+
/** @type {EndpointApi} */
|
|
67
75
|
this.endpointApi = new deps.EndpointApi(this.apiClient);
|
|
68
76
|
} else if (typeof deps.EndpointApi === "object") {
|
|
69
|
-
/** @type {
|
|
77
|
+
/** @type {EndpointApi} */
|
|
70
78
|
this.endpointApi = deps.EndpointApi;
|
|
71
79
|
} else {
|
|
72
80
|
throw new ApiError("deps.EndpointApi doit être une classe ou une instance valide.");
|
|
@@ -184,19 +192,7 @@ export class BaseEntity {
|
|
|
184
192
|
*/
|
|
185
193
|
static fromServerData(data, parent, deps) {
|
|
186
194
|
const instance = new this(parent, {}, deps);
|
|
187
|
-
instance.
|
|
188
|
-
|
|
189
|
-
const { draft, proxy } = instance._buildDraftAndProxy({
|
|
190
|
-
data: { ...data, ...instance.defaultFields },
|
|
191
|
-
serverData: instance._serverData,
|
|
192
|
-
constant: this.SCHEMA_CONSTANTS,
|
|
193
|
-
apiClient: instance.apiClient,
|
|
194
|
-
transforms: instance.transforms,
|
|
195
|
-
removeFields: instance.removeFields
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
instance._draftData = draft;
|
|
199
|
-
instance.data = proxy;
|
|
195
|
+
instance._setData(data);
|
|
200
196
|
return instance;
|
|
201
197
|
}
|
|
202
198
|
|
|
@@ -208,7 +204,7 @@ export class BaseEntity {
|
|
|
208
204
|
* @private
|
|
209
205
|
*/
|
|
210
206
|
_setData(newData) {
|
|
211
|
-
this._serverData = { ...newData };
|
|
207
|
+
this._serverData = reactive({ ...newData });
|
|
212
208
|
|
|
213
209
|
const { draft, proxy } = this._buildDraftAndProxy({
|
|
214
210
|
data: { ...newData, ...this.defaultFields },
|
|
@@ -221,6 +217,10 @@ export class BaseEntity {
|
|
|
221
217
|
this._initialDraftData = JSON.parse(JSON.stringify(draft));
|
|
222
218
|
this._draftData = draft;
|
|
223
219
|
this.data = proxy;
|
|
220
|
+
|
|
221
|
+
if (this._syncReactiveDraft) {
|
|
222
|
+
this.setupReactiveSync();
|
|
223
|
+
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
|
|
@@ -831,6 +831,25 @@ export class BaseEntity {
|
|
|
831
831
|
return Object.keys(changed).length > 0 ? changed : null;
|
|
832
832
|
}
|
|
833
833
|
|
|
834
|
+
/**
|
|
835
|
+
* ───────────────────────────────
|
|
836
|
+
* ReactiveMixin
|
|
837
|
+
* ───────────────────────────────
|
|
838
|
+
*/
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Active la synchronisation réactive entre le brouillon et le schéma.
|
|
842
|
+
*
|
|
843
|
+
* @returns {void}
|
|
844
|
+
* @public
|
|
845
|
+
*/
|
|
846
|
+
setupReactiveSync() {
|
|
847
|
+
if (!this._syncReactiveDraft) {
|
|
848
|
+
this._syncReactiveDraft = true;
|
|
849
|
+
}
|
|
850
|
+
autoSyncDraftFromSchema(this);
|
|
851
|
+
}
|
|
852
|
+
|
|
834
853
|
/**
|
|
835
854
|
* ───────────────────────────────
|
|
836
855
|
* MutualEntityMixin
|
|
@@ -1122,6 +1141,307 @@ export class BaseEntity {
|
|
|
1122
1141
|
}
|
|
1123
1142
|
}
|
|
1124
1143
|
|
|
1144
|
+
/**
|
|
1145
|
+
* Construit dynamiquement des filtres sur un lien entre entités
|
|
1146
|
+
*
|
|
1147
|
+
* @param {string} id - L'ID de l'entité cible.
|
|
1148
|
+
* @param {Object} options - Options de filtrage.
|
|
1149
|
+
* @param {"memberOf" | "projects"} [options.linkType] - Le type de lien (ex: "memberOf", "projects", etc.).
|
|
1150
|
+
* @param {boolean} [options.isAdmin] - Si défini, filtre selon l'existence de isAdmin.
|
|
1151
|
+
* @param {boolean} [options.isAdminPending] - Si défini, filtre selon l'existence de isAdminPending.
|
|
1152
|
+
* @param {boolean} [options.isInviting] - Si défini, filtre selon l'existence de isInviting.
|
|
1153
|
+
* @param {boolean} [options.toBeValidated] - Si défini, filtre selon l'existence de toBeValidated.
|
|
1154
|
+
* @param {Array<string>} [options.roles] - Rôles à filtrer.
|
|
1155
|
+
* @returns {Object} - Un objet de filtres à passer à une requête.
|
|
1156
|
+
* @throws {TypeError} - Si les types des paramètres ne sont pas valides.
|
|
1157
|
+
* @private
|
|
1158
|
+
*/
|
|
1159
|
+
_buildLinkFilters(id, {
|
|
1160
|
+
linkType,
|
|
1161
|
+
isAdmin,
|
|
1162
|
+
isAdminPending,
|
|
1163
|
+
isInviting,
|
|
1164
|
+
toBeValidated = false,
|
|
1165
|
+
roles = []
|
|
1166
|
+
} = {}) {
|
|
1167
|
+
if (typeof id !== "string") throw new TypeError("id doit être une chaîne.");
|
|
1168
|
+
if (typeof linkType !== "string") throw new TypeError("linkType doit être une chaîne.");
|
|
1169
|
+
if (typeof isAdmin !== "undefined" && typeof isAdmin !== "boolean") {
|
|
1170
|
+
throw new TypeError("isAdmin doit être un booléen.");
|
|
1171
|
+
}
|
|
1172
|
+
if (typeof isAdminPending !== "undefined" && typeof isAdminPending !== "boolean") {
|
|
1173
|
+
throw new TypeError("isAdminPending doit être un booléen.");
|
|
1174
|
+
}
|
|
1175
|
+
if (typeof isInviting !== "undefined" && typeof isInviting !== "boolean") {
|
|
1176
|
+
throw new TypeError("isInviting doit être un booléen.");
|
|
1177
|
+
}
|
|
1178
|
+
if (typeof toBeValidated !== "undefined" && typeof toBeValidated !== "boolean") {
|
|
1179
|
+
throw new TypeError("toBeValidated doit être un booléen.");
|
|
1180
|
+
}
|
|
1181
|
+
if (!Array.isArray(roles)) {
|
|
1182
|
+
throw new TypeError("roles doit être un tableau de chaînes.");
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const path = `links.${linkType}.${id}`;
|
|
1186
|
+
const filters = {
|
|
1187
|
+
[`${path}`]: { $exists: true }
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
if (typeof toBeValidated === "boolean") {
|
|
1191
|
+
filters[`${path}.toBeValidated`] = { $exists: toBeValidated };
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (typeof isInviting === "boolean") {
|
|
1195
|
+
filters[`${path}.isInviting`] = { $exists: isInviting };
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
if (isAdmin === true) {
|
|
1199
|
+
filters[`${path}.isAdmin`] = { $exists: true };
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
if(isAdminPending === true) {
|
|
1203
|
+
filters[`${path}.isAdminPending`] = { $exists: true };
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (roles.length > 0) {
|
|
1207
|
+
filters[`${path}.roles`] = { $in: roles };
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
return filters;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Retourne les métadonnées de lien pour une entité :
|
|
1215
|
+
* - `linkType` : clé dans `serverData.links`
|
|
1216
|
+
* - `connectTypeConnect` : valeur envoyée pour `connect()`
|
|
1217
|
+
* - `connectTypeDisconnect` : valeur envoyée pour `disconnect()`
|
|
1218
|
+
* @typedef {Object} LinkMeta
|
|
1219
|
+
* @property {string} linkType
|
|
1220
|
+
* @property {string} connectTypeConnect
|
|
1221
|
+
* @property {string} connectTypeDisconnect
|
|
1222
|
+
* @returns {LinkMeta}
|
|
1223
|
+
* @throws {ApiError} - Si le type d'entité est inconnu.
|
|
1224
|
+
* @private
|
|
1225
|
+
*/
|
|
1226
|
+
_getLinkMeta() {
|
|
1227
|
+
const map = {
|
|
1228
|
+
organizations: {
|
|
1229
|
+
linkType: "memberOf",
|
|
1230
|
+
connectTypeConnect: "member",
|
|
1231
|
+
connectTypeDisconnect: "members"
|
|
1232
|
+
},
|
|
1233
|
+
projects: {
|
|
1234
|
+
linkType: "projects",
|
|
1235
|
+
connectTypeConnect: "contributor",
|
|
1236
|
+
connectTypeDisconnect: "contributors"
|
|
1237
|
+
},
|
|
1238
|
+
events: {
|
|
1239
|
+
linkType: "events",
|
|
1240
|
+
connectTypeConnect: "attendee",
|
|
1241
|
+
connectTypeDisconnect: "attendees"
|
|
1242
|
+
},
|
|
1243
|
+
citoyens: {
|
|
1244
|
+
linkType: "friends",
|
|
1245
|
+
connectTypeConnect: "friend",
|
|
1246
|
+
connectTypeDisconnect: "friends"
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
const entityType = this.getEntityType();
|
|
1251
|
+
const meta = map[entityType];
|
|
1252
|
+
|
|
1253
|
+
if (!meta) {
|
|
1254
|
+
throw new ApiError(`Aucune correspondance de lien définie pour le type d'entité "${entityType}"`);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return meta;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Vérifie si l'entité prend en charge les opérations de lien (`connect`, `disconnect`, etc.).
|
|
1262
|
+
* Utilise `_getLinkMeta()` comme source unique de vérité.
|
|
1263
|
+
*
|
|
1264
|
+
* @throws {ApiError} - Si l'entité ne supporte pas les liens.
|
|
1265
|
+
* @private
|
|
1266
|
+
*/
|
|
1267
|
+
_checkLinkableEntity() {
|
|
1268
|
+
try {
|
|
1269
|
+
this._getLinkMeta(); // si l'entité n'est pas supportée, ça throw déjà
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
if (error instanceof ApiError) {
|
|
1272
|
+
throw new ApiError(`L'entité "${this.getEntityType()}" ne prend pas en charge les actions de lien.`);
|
|
1273
|
+
}
|
|
1274
|
+
throw error;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Retourne le lien de l'utilisateur connecté avec l'entité cible (dans `parent.serverData.links`)
|
|
1280
|
+
* @private
|
|
1281
|
+
*/
|
|
1282
|
+
_getLinkFromConnectedUser() {
|
|
1283
|
+
const { linkType } = this._getLinkMeta();
|
|
1284
|
+
return this.parent?.serverData?.links?.[linkType]?.[this.id] || null;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Soumet une demande de connexion à une entité (ex : membre, contributeur),
|
|
1289
|
+
* ou valide l'invitation si elle existe déjà.
|
|
1290
|
+
*
|
|
1291
|
+
* @returns {Promise<Object>} - Résultat de l'API
|
|
1292
|
+
* @throws {ApiError} - En cas d'erreur de connexion ou d'invitation.
|
|
1293
|
+
* @private
|
|
1294
|
+
*/
|
|
1295
|
+
async _submitLinkRequest() {
|
|
1296
|
+
|
|
1297
|
+
if (!this.id) {
|
|
1298
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const { connectTypeConnect } = this._getLinkMeta();
|
|
1302
|
+
|
|
1303
|
+
const userLink = this._getLinkFromConnectedUser();
|
|
1304
|
+
|
|
1305
|
+
// Cas : aucun lien → on demande à se connecter
|
|
1306
|
+
if (!userLink) {
|
|
1307
|
+
const data = {
|
|
1308
|
+
parentType: this.getEntityType(),
|
|
1309
|
+
parentId: this.id,
|
|
1310
|
+
connectType: connectTypeConnect
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1314
|
+
const retour = await this.callIsMe(() => this.endpointApi.connect(data));
|
|
1315
|
+
await this.parent.refresh();
|
|
1316
|
+
return retour;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Cas : invitation reçue → on valide l'invitation
|
|
1320
|
+
if (userLink.isInviting) {
|
|
1321
|
+
return this._acceptLinkRequest();
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Cas : déjà en attente
|
|
1325
|
+
if (userLink.toBeValidated) {
|
|
1326
|
+
throw new ApiError("Vous êtes déjà en attente de validation.");
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Cas par défaut : rien à faire
|
|
1330
|
+
throw new ApiError("Vous êtes déjà connecté à cette entité.");
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Soumet une demande pour devenir administrateur d'une entité.
|
|
1335
|
+
*
|
|
1336
|
+
* @returns {Promise<Object>}
|
|
1337
|
+
* @throws {ApiError}
|
|
1338
|
+
* @private
|
|
1339
|
+
*/
|
|
1340
|
+
async _submitLinkRequestAdmin() {
|
|
1341
|
+
|
|
1342
|
+
if (!this.id) {
|
|
1343
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const userLink = this._getLinkFromConnectedUser();
|
|
1347
|
+
|
|
1348
|
+
// Aucun lien existant → envoie une demande avec rôle "admin"
|
|
1349
|
+
if (!userLink) {
|
|
1350
|
+
const data = {
|
|
1351
|
+
parentType: this.getEntityType(),
|
|
1352
|
+
parentId: this.id,
|
|
1353
|
+
connectType: "admin"
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1357
|
+
const retour = await this.callIsMe(() => this.endpointApi.connect(data));
|
|
1358
|
+
await this.parent.refresh();
|
|
1359
|
+
return retour;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// Invitation reçue → accepte automatiquement
|
|
1363
|
+
if (userLink.isInviting) {
|
|
1364
|
+
return this._acceptLinkRequest();
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Déjà en attente pour admin
|
|
1368
|
+
if (userLink.toBeValidated && userLink.isAdminPending) {
|
|
1369
|
+
throw new ApiError("Vous êtes déjà en attente de validation pour devenir admin.");
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// Déjà en attente pour un autre rôle
|
|
1373
|
+
if (userLink.toBeValidated) {
|
|
1374
|
+
throw new ApiError("Vous êtes déjà en attente de validation pour rejoindre cette entité.");
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Déjà connecté
|
|
1378
|
+
throw new ApiError("Vous êtes déjà connecté à cette entité.");
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Accepte une demande de lien (ex : invitation à rejoindre un groupe).
|
|
1383
|
+
*
|
|
1384
|
+
* @returns {Promise<Object>} - Résultat de l'API
|
|
1385
|
+
* @throws {ApiError} - En cas d'erreur de connexion ou d'invitation.
|
|
1386
|
+
* @private
|
|
1387
|
+
*/
|
|
1388
|
+
async _acceptLinkRequest() {
|
|
1389
|
+
|
|
1390
|
+
if (!this.id) {
|
|
1391
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
const userLink = this._getLinkFromConnectedUser();
|
|
1395
|
+
|
|
1396
|
+
if (userLink.isInviting) {
|
|
1397
|
+
const data = {
|
|
1398
|
+
parentType: this.getEntityType(),
|
|
1399
|
+
parentId: this.id,
|
|
1400
|
+
linkOption: "isInviting"
|
|
1401
|
+
};
|
|
1402
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1403
|
+
const retour = await this.callIsMe(() => this.endpointApi.linkValidate(data));
|
|
1404
|
+
await this.parent.refresh();
|
|
1405
|
+
return retour;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
throw new ApiError("Vous n'avez pas d'invitation à valider.");
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* Annule une demande de lien ou se déconnecte d'une entité.
|
|
1413
|
+
*
|
|
1414
|
+
* @returns {Promise<Object>} - Résultat de l'API
|
|
1415
|
+
* @throws {ApiError} - En cas d'erreur de connexion ou d'invitation.
|
|
1416
|
+
* @private
|
|
1417
|
+
*/
|
|
1418
|
+
async _leaveLinkRequest() {
|
|
1419
|
+
|
|
1420
|
+
if (!this.id) {
|
|
1421
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
const { connectTypeDisconnect } = this._getLinkMeta();
|
|
1425
|
+
|
|
1426
|
+
const userLink = this._getLinkFromConnectedUser();
|
|
1427
|
+
|
|
1428
|
+
if (!userLink) {
|
|
1429
|
+
throw new ApiError("Vous n'êtes pas connecté à cette entité.");
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
const data = {
|
|
1433
|
+
parentType: this.getEntityType(),
|
|
1434
|
+
parentId: this.id,
|
|
1435
|
+
// Normalement en auto dans le schema mais je le met quand même
|
|
1436
|
+
connectType: connectTypeDisconnect
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1440
|
+
const retour = await this.callIsMe(() => this.endpointApi.disconnect(data));
|
|
1441
|
+
await this.parent.refresh();
|
|
1442
|
+
return retour;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1125
1445
|
/**
|
|
1126
1446
|
* ───────────────────────────────
|
|
1127
1447
|
* EntityMixin
|
|
@@ -1543,6 +1863,128 @@ export class BaseEntity {
|
|
|
1543
1863
|
return this._createFilteredProxy(rawList);
|
|
1544
1864
|
}
|
|
1545
1865
|
|
|
1866
|
+
/**
|
|
1867
|
+
* Soumet une demande pour rejoindre l'entité courante (ex. organisation, projet, événement...).
|
|
1868
|
+
* Si une invitation est en attente, elle est automatiquement acceptée.
|
|
1869
|
+
*
|
|
1870
|
+
* @returns {Promise<Object>} - Résultat de la demande ou de la validation.
|
|
1871
|
+
* @throws {ApiError} - Si l'entité ne supporte pas l'action ou si une demande est déjà en cours.
|
|
1872
|
+
*/
|
|
1873
|
+
async requestToJoin(){
|
|
1874
|
+
if (!this.isMe) {
|
|
1875
|
+
throw new ApiError("Vous devez être connecté pour envoyer une demande de participation.");
|
|
1876
|
+
}
|
|
1877
|
+
this._checkLinkableEntity();
|
|
1878
|
+
return this._submitLinkRequest();
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* Soumet une demande pour devenir administrateur de l'entité courante.
|
|
1883
|
+
* Si une invitation est en attente, elle est automatiquement validée.
|
|
1884
|
+
*
|
|
1885
|
+
* @returns {Promise<Object>} - Résultat de la demande ou de la validation.
|
|
1886
|
+
* @throws {ApiError} - Si l'entité ne supporte pas l'action ou si une demande est déjà en cours.
|
|
1887
|
+
*/
|
|
1888
|
+
async requestToJoinAdmin(){
|
|
1889
|
+
if (!this.isMe) {
|
|
1890
|
+
throw new ApiError("Vous devez être connecté pour envoyer une demande de participation.");
|
|
1891
|
+
}
|
|
1892
|
+
this._checkLinkableEntity();
|
|
1893
|
+
return this._submitLinkRequestAdmin();
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
/**
|
|
1897
|
+
* Accepte une invitation à rejoindre l'entité courante.
|
|
1898
|
+
* Ne fonctionne que si un lien avec `isInviting` est détecté.
|
|
1899
|
+
*
|
|
1900
|
+
* @returns {Promise<Object>} - Résultat de l'acceptation de l'invitation.
|
|
1901
|
+
* @throws {ApiError} - Si aucune invitation n'est en attente ou si l'entité ne supporte pas cette action.
|
|
1902
|
+
*/
|
|
1903
|
+
async acceptInvitation(){
|
|
1904
|
+
if (!this.isMe) {
|
|
1905
|
+
throw new ApiError("Vous devez être connecté pour envoyer une demande de participation.");
|
|
1906
|
+
}
|
|
1907
|
+
this._checkLinkableEntity();
|
|
1908
|
+
return this._acceptLinkRequest();
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Se désengage de l'entité courante : quitte un rôle (membre, contributeur, etc.).
|
|
1913
|
+
*
|
|
1914
|
+
* @returns {Promise<Object>} - Résultat de la déconnexion.
|
|
1915
|
+
* @throws {ApiError} - Si l'entité ne supporte pas l'action ou si l'utilisateur n'est pas lié à cette entité.
|
|
1916
|
+
*/
|
|
1917
|
+
async leave() {
|
|
1918
|
+
if (!this.isMe) {
|
|
1919
|
+
throw new ApiError("Vous devez être connecté pour envoyer une demande de participation.");
|
|
1920
|
+
}
|
|
1921
|
+
this._checkLinkableEntity();
|
|
1922
|
+
return this._leaveLinkRequest();
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* S'abonne à l'entité courante.
|
|
1927
|
+
*
|
|
1928
|
+
* @returns {Promise<Object>} - Résultat de l'abonnement.
|
|
1929
|
+
* @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée.
|
|
1930
|
+
*/
|
|
1931
|
+
async follow() {
|
|
1932
|
+
if (!this.isMe) {
|
|
1933
|
+
throw new ApiError("Vous devez être connecté pour suivre cette entité.");
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
if (!this.id) {
|
|
1937
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const userLink = this.parent?.serverData?.links?.["follows"]?.[this.id] || null;
|
|
1941
|
+
|
|
1942
|
+
if (!userLink) {
|
|
1943
|
+
const data = {
|
|
1944
|
+
parentType: this.getEntityType(),
|
|
1945
|
+
parentId: this.id
|
|
1946
|
+
};
|
|
1947
|
+
const retour = await this.callIsMe(() => this.endpointApi.follow(data));
|
|
1948
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1949
|
+
await this.parent.refresh();
|
|
1950
|
+
return retour;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
throw new ApiError("Vous êtes déjà abonné à cette entité.");
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Se désabonne de l'entité courante.
|
|
1958
|
+
*
|
|
1959
|
+
* @returns {Promise<Object>} - Résultat de la désinscription.
|
|
1960
|
+
* @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée.
|
|
1961
|
+
*/
|
|
1962
|
+
async unfollow() {
|
|
1963
|
+
if (!this.isMe) {
|
|
1964
|
+
throw new ApiError("Vous devez être connecté pour vous désabonner.");
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
if (!this.id) {
|
|
1968
|
+
throw new ApiError(`${this.constructor.name} non enregistrée.`);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
const userLink = this.parent?.serverData?.links?.["follows"]?.[this.id] || null;
|
|
1972
|
+
|
|
1973
|
+
if (userLink) {
|
|
1974
|
+
const data = {
|
|
1975
|
+
parentType: this.getEntityType(),
|
|
1976
|
+
parentId: this.id,
|
|
1977
|
+
connectType: "followers"
|
|
1978
|
+
};
|
|
1979
|
+
const retour = await this.callIsMe(() => this.endpointApi.disconnect(data));
|
|
1980
|
+
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
1981
|
+
await this.parent.refresh();
|
|
1982
|
+
return retour;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
throw new ApiError("Vous n'êtes pas abonné à cette entité.");
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1546
1988
|
}
|
|
1547
1989
|
|
|
1548
1990
|
export default BaseEntity;
|