@communecter/cocolight-api-client 1.0.154 → 1.0.156
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/1035.cocolight-api-client.browser.js +1 -1
- package/dist/1035.cocolight-api-client.cjs +1 -1
- package/dist/1483.cocolight-api-client.browser.js +1 -1
- package/dist/1483.cocolight-api-client.cjs +1 -1
- package/dist/1491.cocolight-api-client.browser.js +1 -1
- package/dist/1491.cocolight-api-client.cjs +1 -1
- package/dist/1494.cocolight-api-client.browser.js +1 -1
- package/dist/1494.cocolight-api-client.cjs +1 -1
- package/dist/1641.cocolight-api-client.browser.js +1 -1
- package/dist/1641.cocolight-api-client.cjs +1 -1
- package/dist/1678.cocolight-api-client.browser.js +1 -1
- package/dist/1678.cocolight-api-client.cjs +1 -1
- package/dist/2041.cocolight-api-client.browser.js +1 -1
- package/dist/2041.cocolight-api-client.cjs +1 -1
- package/dist/2302.cocolight-api-client.browser.js +1 -1
- package/dist/2302.cocolight-api-client.cjs +1 -1
- package/dist/2313.cocolight-api-client.browser.js +1 -1
- package/dist/2313.cocolight-api-client.cjs +1 -1
- package/dist/2379.cocolight-api-client.browser.js +1 -1
- package/dist/2379.cocolight-api-client.cjs +1 -1
- package/dist/2439.cocolight-api-client.browser.js +1 -1
- package/dist/2439.cocolight-api-client.cjs +1 -1
- package/dist/2594.cocolight-api-client.browser.js +1 -1
- package/dist/2594.cocolight-api-client.cjs +1 -1
- package/dist/2766.cocolight-api-client.browser.js +1 -1
- package/dist/2766.cocolight-api-client.cjs +1 -1
- package/dist/2812.cocolight-api-client.browser.js +1 -1
- package/dist/2812.cocolight-api-client.cjs +1 -1
- package/dist/2918.cocolight-api-client.browser.js +1 -1
- package/dist/2918.cocolight-api-client.cjs +1 -1
- package/dist/3028.cocolight-api-client.browser.js +1 -1
- package/dist/3028.cocolight-api-client.cjs +1 -1
- package/dist/3330.cocolight-api-client.browser.js +1 -1
- package/dist/3330.cocolight-api-client.cjs +1 -1
- package/dist/3371.cocolight-api-client.browser.js +1 -1
- package/dist/3371.cocolight-api-client.cjs +1 -1
- package/dist/3401.cocolight-api-client.browser.js +1 -1
- package/dist/3401.cocolight-api-client.cjs +1 -1
- package/dist/3516.cocolight-api-client.browser.js +1 -1
- package/dist/3516.cocolight-api-client.cjs +1 -1
- package/dist/3593.cocolight-api-client.browser.js +1 -1
- package/dist/3593.cocolight-api-client.cjs +1 -1
- package/dist/3764.cocolight-api-client.browser.js +1 -1
- package/dist/3764.cocolight-api-client.cjs +1 -1
- package/dist/3873.cocolight-api-client.browser.js +1 -1
- package/dist/3873.cocolight-api-client.cjs +1 -1
- package/dist/3911.cocolight-api-client.browser.js +1 -1
- package/dist/3911.cocolight-api-client.cjs +1 -1
- package/dist/4031.cocolight-api-client.browser.js +1 -1
- package/dist/4031.cocolight-api-client.cjs +1 -1
- package/dist/4152.cocolight-api-client.browser.js +1 -1
- package/dist/4152.cocolight-api-client.cjs +1 -1
- package/dist/4203.cocolight-api-client.browser.js +1 -1
- package/dist/4203.cocolight-api-client.cjs +1 -1
- package/dist/424.cocolight-api-client.browser.js +1 -1
- package/dist/424.cocolight-api-client.cjs +1 -1
- package/dist/435.cocolight-api-client.browser.js +1 -1
- package/dist/435.cocolight-api-client.cjs +1 -1
- package/dist/4402.cocolight-api-client.browser.js +1 -1
- package/dist/4402.cocolight-api-client.cjs +1 -1
- package/dist/4425.cocolight-api-client.browser.js +1 -1
- package/dist/4425.cocolight-api-client.cjs +1 -1
- package/dist/4472.cocolight-api-client.browser.js +1 -1
- package/dist/4472.cocolight-api-client.cjs +1 -1
- package/dist/4914.cocolight-api-client.browser.js +1 -1
- package/dist/4914.cocolight-api-client.cjs +1 -1
- package/dist/4959.cocolight-api-client.browser.js +1 -1
- package/dist/4959.cocolight-api-client.cjs +1 -1
- package/dist/5100.cocolight-api-client.browser.js +1 -1
- package/dist/5100.cocolight-api-client.cjs +1 -1
- package/dist/5111.cocolight-api-client.browser.js +1 -1
- package/dist/5111.cocolight-api-client.cjs +1 -1
- package/dist/5286.cocolight-api-client.browser.js +1 -1
- package/dist/5286.cocolight-api-client.cjs +1 -1
- package/dist/5380.cocolight-api-client.browser.js +1 -1
- package/dist/5380.cocolight-api-client.cjs +1 -1
- package/dist/5383.cocolight-api-client.browser.js +1 -1
- package/dist/5383.cocolight-api-client.cjs +1 -1
- package/dist/5480.cocolight-api-client.browser.js +1 -1
- package/dist/5480.cocolight-api-client.cjs +1 -1
- package/dist/5541.cocolight-api-client.browser.js +1 -1
- package/dist/5541.cocolight-api-client.cjs +1 -1
- package/dist/5839.cocolight-api-client.browser.js +1 -1
- package/dist/5839.cocolight-api-client.cjs +1 -1
- package/dist/5862.cocolight-api-client.browser.js +1 -1
- package/dist/5862.cocolight-api-client.cjs +1 -1
- package/dist/5869.cocolight-api-client.browser.js +1 -1
- package/dist/5869.cocolight-api-client.cjs +1 -1
- package/dist/588.cocolight-api-client.browser.js +1 -1
- package/dist/588.cocolight-api-client.cjs +1 -1
- package/dist/601.cocolight-api-client.browser.js +1 -1
- package/dist/601.cocolight-api-client.cjs +1 -1
- package/dist/6012.cocolight-api-client.browser.js +1 -1
- package/dist/6012.cocolight-api-client.cjs +1 -1
- package/dist/6200.cocolight-api-client.browser.js +1 -1
- package/dist/6200.cocolight-api-client.cjs +1 -1
- package/dist/6369.cocolight-api-client.browser.js +1 -1
- package/dist/6369.cocolight-api-client.cjs +1 -1
- package/dist/7009.cocolight-api-client.browser.js +1 -1
- package/dist/7009.cocolight-api-client.cjs +1 -1
- package/dist/725.cocolight-api-client.browser.js +1 -1
- package/dist/725.cocolight-api-client.cjs +1 -1
- package/dist/7320.cocolight-api-client.browser.js +1 -1
- package/dist/7339.cocolight-api-client.browser.js +1 -1
- package/dist/7339.cocolight-api-client.cjs +1 -1
- package/dist/7398.cocolight-api-client.browser.js +1 -1
- package/dist/7398.cocolight-api-client.cjs +1 -1
- package/dist/7888.cocolight-api-client.browser.js +1 -1
- package/dist/7888.cocolight-api-client.cjs +1 -1
- package/dist/7975.cocolight-api-client.browser.js +1 -1
- package/dist/7975.cocolight-api-client.cjs +1 -1
- package/dist/809.cocolight-api-client.browser.js +1 -1
- package/dist/809.cocolight-api-client.cjs +1 -1
- package/dist/8300.cocolight-api-client.browser.js +1 -1
- package/dist/8300.cocolight-api-client.cjs +1 -1
- package/dist/8317.cocolight-api-client.browser.js +1 -1
- package/dist/8317.cocolight-api-client.cjs +1 -1
- package/dist/8525.cocolight-api-client.browser.js +1 -1
- package/dist/8525.cocolight-api-client.cjs +1 -1
- package/dist/8894.cocolight-api-client.browser.js +1 -1
- package/dist/8894.cocolight-api-client.cjs +1 -1
- package/dist/9301.cocolight-api-client.browser.js +1 -1
- package/dist/9301.cocolight-api-client.cjs +1 -1
- package/dist/9438.cocolight-api-client.browser.js +1 -1
- package/dist/9438.cocolight-api-client.cjs +1 -1
- package/dist/9478.cocolight-api-client.browser.js +1 -1
- package/dist/9478.cocolight-api-client.cjs +1 -1
- package/dist/9568.cocolight-api-client.browser.js +1 -1
- package/dist/9568.cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.browser.js +6 -6
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.vite.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
- package/package.json +7 -17
- package/types/Api.d.ts +1 -0
- package/types/ApiClient.d.ts +10 -1
- package/types/api/BaseEntity.d.ts +24 -0
- package/types/api/EndpointApi.d.ts +4 -4
- package/types/api/EndpointApi.types.d.ts +3 -3
- package/types/utils/OfflineClientManager.d.ts +8 -0
- package/dist/1035.cocolight-api-client.mjs.js +0 -1
- package/dist/1483.cocolight-api-client.mjs.js +0 -1
- package/dist/1491.cocolight-api-client.mjs.js +0 -1
- package/dist/1494.cocolight-api-client.mjs.js +0 -1
- package/dist/1641.cocolight-api-client.mjs.js +0 -1
- package/dist/1678.cocolight-api-client.mjs.js +0 -1
- package/dist/2041.cocolight-api-client.mjs.js +0 -1
- package/dist/2302.cocolight-api-client.mjs.js +0 -1
- package/dist/2313.cocolight-api-client.mjs.js +0 -1
- package/dist/2379.cocolight-api-client.mjs.js +0 -1
- package/dist/2439.cocolight-api-client.mjs.js +0 -1
- package/dist/2594.cocolight-api-client.mjs.js +0 -1
- package/dist/2766.cocolight-api-client.mjs.js +0 -1
- package/dist/2812.cocolight-api-client.mjs.js +0 -1
- package/dist/2918.cocolight-api-client.mjs.js +0 -1
- package/dist/3028.cocolight-api-client.mjs.js +0 -1
- package/dist/3330.cocolight-api-client.mjs.js +0 -1
- package/dist/3371.cocolight-api-client.mjs.js +0 -1
- package/dist/3401.cocolight-api-client.mjs.js +0 -1
- package/dist/3516.cocolight-api-client.mjs.js +0 -1
- package/dist/3593.cocolight-api-client.mjs.js +0 -1
- package/dist/3764.cocolight-api-client.mjs.js +0 -1
- package/dist/3873.cocolight-api-client.mjs.js +0 -1
- package/dist/3911.cocolight-api-client.mjs.js +0 -1
- package/dist/4031.cocolight-api-client.mjs.js +0 -1
- package/dist/4152.cocolight-api-client.mjs.js +0 -1
- package/dist/4203.cocolight-api-client.mjs.js +0 -1
- package/dist/424.cocolight-api-client.mjs.js +0 -1
- package/dist/435.cocolight-api-client.mjs.js +0 -1
- package/dist/4402.cocolight-api-client.mjs.js +0 -1
- package/dist/4425.cocolight-api-client.mjs.js +0 -1
- package/dist/4472.cocolight-api-client.mjs.js +0 -1
- package/dist/4914.cocolight-api-client.mjs.js +0 -1
- package/dist/4959.cocolight-api-client.mjs.js +0 -1
- package/dist/5100.cocolight-api-client.mjs.js +0 -1
- package/dist/5111.cocolight-api-client.mjs.js +0 -1
- package/dist/5286.cocolight-api-client.mjs.js +0 -1
- package/dist/5380.cocolight-api-client.mjs.js +0 -1
- package/dist/5383.cocolight-api-client.mjs.js +0 -1
- package/dist/5480.cocolight-api-client.mjs.js +0 -1
- package/dist/5541.cocolight-api-client.mjs.js +0 -1
- package/dist/5839.cocolight-api-client.mjs.js +0 -1
- package/dist/5862.cocolight-api-client.mjs.js +0 -1
- package/dist/5869.cocolight-api-client.mjs.js +0 -1
- package/dist/588.cocolight-api-client.mjs.js +0 -1
- package/dist/601.cocolight-api-client.mjs.js +0 -1
- package/dist/6012.cocolight-api-client.mjs.js +0 -1
- package/dist/6200.cocolight-api-client.mjs.js +0 -1
- package/dist/6369.cocolight-api-client.mjs.js +0 -1
- package/dist/7009.cocolight-api-client.mjs.js +0 -1
- package/dist/725.cocolight-api-client.mjs.js +0 -1
- package/dist/7339.cocolight-api-client.mjs.js +0 -1
- package/dist/7398.cocolight-api-client.mjs.js +0 -1
- package/dist/7888.cocolight-api-client.mjs.js +0 -1
- package/dist/7975.cocolight-api-client.mjs.js +0 -1
- package/dist/809.cocolight-api-client.mjs.js +0 -1
- package/dist/8300.cocolight-api-client.mjs.js +0 -1
- package/dist/8317.cocolight-api-client.mjs.js +0 -1
- package/dist/8525.cocolight-api-client.mjs.js +0 -1
- package/dist/8894.cocolight-api-client.mjs.js +0 -1
- package/dist/9301.cocolight-api-client.mjs.js +0 -1
- package/dist/9438.cocolight-api-client.mjs.js +0 -1
- package/dist/9478.cocolight-api-client.mjs.js +0 -1
- package/dist/9568.cocolight-api-client.mjs.js +0 -1
- package/dist/cocolight-api-client.mjs.js +0 -1
- package/src/Api.ts +0 -348
- package/src/ApiClient.ts +0 -1994
- package/src/EJSONType.ts +0 -103
- package/src/api/Action.ts +0 -665
- package/src/api/Answer.ts +0 -1392
- package/src/api/Badge.ts +0 -143
- package/src/api/BaseEntity.ts +0 -6338
- package/src/api/Classified.ts +0 -175
- package/src/api/Comment.ts +0 -280
- package/src/api/EndpointApi.ts +0 -2270
- package/src/api/EndpointApi.types.ts +0 -6566
- package/src/api/EntityRegistry.ts +0 -324
- package/src/api/Event.ts +0 -360
- package/src/api/Form.ts +0 -282
- package/src/api/News.ts +0 -547
- package/src/api/Notifications.ts +0 -196
- package/src/api/Organization.ts +0 -771
- package/src/api/Poi.ts +0 -242
- package/src/api/Project.ts +0 -545
- package/src/api/User.ts +0 -1889
- package/src/api/UserApi.ts +0 -192
- package/src/api/serverDataType/Action.ts +0 -177
- package/src/api/serverDataType/Answer.ts +0 -106
- package/src/api/serverDataType/Classified.ts +0 -152
- package/src/api/serverDataType/Comment.ts +0 -88
- package/src/api/serverDataType/Country.ts +0 -1
- package/src/api/serverDataType/Event.ts +0 -112
- package/src/api/serverDataType/Form.ts +0 -157
- package/src/api/serverDataType/News.ts +0 -138
- package/src/api/serverDataType/Notification.ts +0 -81
- package/src/api/serverDataType/Organization.ts +0 -111
- package/src/api/serverDataType/Poi.ts +0 -75
- package/src/api/serverDataType/Project.ts +0 -115
- package/src/api/serverDataType/User.ts +0 -112
- package/src/api/serverDataType/Zone.ts +0 -33
- package/src/api/serverDataType/common.ts +0 -84
- package/src/costum/normalize.ts +0 -7
- package/src/costum/registry.ts +0 -138
- package/src/costum/runtime-map.ts +0 -221
- package/src/costum/runtime.ts +0 -274
- package/src/costum/schemas/CercleTravailleursSociauxLiberauxLaPlumeSociale.ts +0 -19
- package/src/costum/schemas/FabEn.ts +0 -19
- package/src/costum/schemas/LivingLabInnovationSocialeBraspanon.ts +0 -19
- package/src/costum/schemas/alfph.ts +0 -27
- package/src/costum/schemas/campagne.ts +0 -19
- package/src/costum/schemas/chtitube.ts +0 -19
- package/src/costum/schemas/circuitCourt.ts +0 -19
- package/src/costum/schemas/cmcasHauteBretagne.ts +0 -19
- package/src/costum/schemas/coFinancementTierslieux.ts +0 -27
- package/src/costum/schemas/coeurNumerique.ts +0 -19
- package/src/costum/schemas/communLundi.ts +0 -19
- package/src/costum/schemas/cressReunion.ts +0 -35
- package/src/costum/schemas/cressReunionNetwork.ts +0 -35
- package/src/costum/schemas/ctenat.ts +0 -43
- package/src/costum/schemas/cyberReunion.ts +0 -19
- package/src/costum/schemas/ekisphere.ts +0 -35
- package/src/costum/schemas/equipementsSportifs974.ts +0 -19
- package/src/costum/schemas/federationDesCae.ts +0 -19
- package/src/costum/schemas/formationGenerique.ts +0 -35
- package/src/costum/schemas/franceTierslieux.ts +0 -19
- package/src/costum/schemas/hinaura.ts +0 -27
- package/src/costum/schemas/hubmayotte.ts +0 -19
- package/src/costum/schemas/hubnouvellecaledonie.ts +0 -19
- package/src/costum/schemas/hubpolynesiefrancaise.ts +0 -19
- package/src/costum/schemas/hubreunion.ts +0 -19
- package/src/costum/schemas/hubterritoires.ts +0 -19
- package/src/costum/schemas/hubwallisetfutuna.ts +0 -19
- package/src/costum/schemas/hva.ts +0 -27
- package/src/costum/schemas/ibv2.ts +0 -19
- package/src/costum/schemas/institutBleu.ts +0 -19
- package/src/costum/schemas/journalInsoumisChambery.ts +0 -27
- package/src/costum/schemas/kpacite.ts +0 -19
- package/src/costum/schemas/laCompagnieDesTierslieux.ts +0 -43
- package/src/costum/schemas/laMaisonDesFaiseurs.ts +0 -19
- package/src/costum/schemas/laReunionPourUnNumeriqueInclusif.ts +0 -19
- package/src/costum/schemas/laSemaineDesTierslieux.ts +0 -19
- package/src/costum/schemas/labEnCommuns.ts +0 -19
- package/src/costum/schemas/lakou.ts +0 -27
- package/src/costum/schemas/lesCommunsDesTierslieux.ts +0 -19
- package/src/costum/schemas/mayottePourUnNumeriqueInclusif.ts +0 -19
- package/src/costum/schemas/meir.ts +0 -27
- package/src/costum/schemas/navigatorDesTierslieux.ts +0 -19
- package/src/costum/schemas/notragora.ts +0 -27
- package/src/costum/schemas/nouvellecaledoniePourUnNumeriqueInclusif.ts +0 -19
- package/src/costum/schemas/openAtlas.ts +0 -19
- package/src/costum/schemas/polynesieFrancaisePourUnNumeriqueInclusif.ts +0 -19
- package/src/costum/schemas/prdr.ts +0 -19
- package/src/costum/schemas/reseauTierslieux.ts +0 -19
- package/src/costum/schemas/rfflabs.ts +0 -19
- package/src/costum/schemas/ries.ts +0 -19
- package/src/costum/schemas/siteDuPactePourLaTransition.ts +0 -27
- package/src/costum/schemas/sportSanteBienetre.ts +0 -43
- package/src/costum/schemas/suppa.ts +0 -19
- package/src/costum/schemas/terramies.ts +0 -19
- package/src/costum/schemas/testeTplFormation.ts +0 -35
- package/src/costum/schemas/tierslieuxbelgique.ts +0 -19
- package/src/costum/schemas/tierslieuxorg1.ts +0 -35
- package/src/costum/schemas/transiter.ts +0 -35
- package/src/costum/schemas/vhelioPeiTour2025.ts +0 -19
- package/src/costum/schemas/wallisetfutunaPourUnNumeriqueInclusif.ts +0 -19
- package/src/costum/symbol.ts +0 -26
- package/src/costum/types.ts +0 -895
- package/src/endpoints.module.ts +0 -4253
- package/src/error.ts +0 -86
- package/src/forms/entityForm.ts +0 -142
- package/src/index.ts +0 -161
- package/src/mixin/UserMixin.ts +0 -4
- package/src/types/api-responses.ts +0 -217
- package/src/types/entities.ts +0 -58
- package/src/types/error-guards.ts +0 -230
- package/src/types/index.ts +0 -41
- package/src/types/payloads.ts +0 -21
- package/src/types/transforms.ts +0 -119
- package/src/utils/FileOfflineStorageStrategy.node.ts +0 -50
- package/src/utils/FileStorageStrategy.node.ts +0 -63
- package/src/utils/MultiServerFileStorageStrategy.node.ts +0 -67
- package/src/utils/MultiServerTokenStorageStrategy.ts +0 -139
- package/src/utils/OfflineClientManager.ts +0 -216
- package/src/utils/OfflineQueueStorageStrategy.ts +0 -47
- package/src/utils/TokenStorage.ts +0 -77
- package/src/utils/compat.ts +0 -12
- package/src/utils/createDefaultMultiServerTokenStorageStrategy.ts +0 -35
- package/src/utils/createDefaultOfflineStrategy.ts +0 -34
- package/src/utils/createDefaultTokenStorageStrategy.ts +0 -33
- package/src/utils/reactive.ts +0 -243
- package/src/utils/stream-utils.node.ts +0 -12
package/src/api/User.ts
DELETED
|
@@ -1,1889 +0,0 @@
|
|
|
1
|
-
import { ApiError } from "../error.js";
|
|
2
|
-
import { BaseEntity } from "./BaseEntity.js";
|
|
3
|
-
import { Notifications } from "./Notifications.js";
|
|
4
|
-
import { resolveCostumScope, costumScopeFromEntity, type CostumScope } from "../costum/runtime.js";
|
|
5
|
-
import { UserMixin } from "../mixin/UserMixin.js";
|
|
6
|
-
import { createSocialTransform } from "../types/transforms.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import type { Badge } from "./Badge.js";
|
|
10
|
-
import type { PaginatorPage, PaginatorState } from "./BaseEntity.js";
|
|
11
|
-
import type {
|
|
12
|
-
ConnectData,
|
|
13
|
-
DisconnectData,
|
|
14
|
-
LinkValidateData,
|
|
15
|
-
FollowData,
|
|
16
|
-
ChangePasswordData,
|
|
17
|
-
DeleteAccountData,
|
|
18
|
-
GetSubscriptionsAdminData,
|
|
19
|
-
GetSubscriptionsData,
|
|
20
|
-
GetOrganizationsNoAdminData,
|
|
21
|
-
GetOrganizationsAdminData,
|
|
22
|
-
GetUserEligiblePlacesData,
|
|
23
|
-
GetFriendsAdminData,
|
|
24
|
-
DemoteAdminData,
|
|
25
|
-
GetNotificationsData,
|
|
26
|
-
GetNotificationsCountData,
|
|
27
|
-
NotificationUpdateData,
|
|
28
|
-
MarkNotificationAsReadData
|
|
29
|
-
} from "./EndpointApi.types.js";
|
|
30
|
-
import type { Organization } from "./Organization.js";
|
|
31
|
-
import type { KnownCostumSlug } from "../costum/types.js";
|
|
32
|
-
import type { NotificationItemData } from "./serverDataType/Notification.js";
|
|
33
|
-
import type { EntityTypes } from "@/types/entities.js";
|
|
34
|
-
|
|
35
|
-
type ApiClient = import("../ApiClient.js").default;
|
|
36
|
-
type UserItemNormalized = import("./serverDataType/User.js").UserItemNormalized;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Interface représentant le lien entre un utilisateur et une entité parente.
|
|
40
|
-
* Utilisée pour typer les données de relation (membre, admin, invitations, etc.)
|
|
41
|
-
*/
|
|
42
|
-
interface ParentLink {
|
|
43
|
-
isAdmin?: boolean;
|
|
44
|
-
isAdminPending?: boolean;
|
|
45
|
-
isInviting?: boolean;
|
|
46
|
-
isAdminInviting?: boolean;
|
|
47
|
-
toBeValidated?: boolean;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class User extends BaseEntity<UserItemNormalized> {
|
|
51
|
-
|
|
52
|
-
static override entityType = "citoyens" as const;
|
|
53
|
-
|
|
54
|
-
static override entityTag = "User";
|
|
55
|
-
|
|
56
|
-
static override SCHEMA_CONSTANTS: string[] = [
|
|
57
|
-
"UPDATE_BLOCK_DESCRIPTION",
|
|
58
|
-
"UPDATE_BLOCK_INFO",
|
|
59
|
-
"UPDATE_BLOCK_SOCIAL",
|
|
60
|
-
"UPDATE_BLOCK_LOCALITY",
|
|
61
|
-
"UPDATE_BLOCK_SLUG",
|
|
62
|
-
"PROFIL_IMAGE"
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
static UPDATE_BLOCKS = new Map([
|
|
66
|
-
["UPDATE_BLOCK_DESCRIPTION", "updateDescription"],
|
|
67
|
-
["UPDATE_BLOCK_SOCIAL", "updateSocial"],
|
|
68
|
-
["UPDATE_BLOCK_LOCALITY", "updateLocality"],
|
|
69
|
-
["UPDATE_BLOCK_INFO", "updateInfo"],
|
|
70
|
-
["UPDATE_BLOCK_SLUG", "updateSlug"],
|
|
71
|
-
["PROFIL_IMAGE", "updateImageProfil"]
|
|
72
|
-
] as const);
|
|
73
|
-
|
|
74
|
-
override defaultFields: Record<string, any> = {
|
|
75
|
-
typeElement: this.getEntityType(),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
override removeFields: string[] = [
|
|
79
|
-
"typeElement",
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
override transforms: Record<string, (val: any, full: any) => any> = {
|
|
83
|
-
github: createSocialTransform("github"),
|
|
84
|
-
gitlab: createSocialTransform("gitlab"),
|
|
85
|
-
facebook: createSocialTransform("facebook"),
|
|
86
|
-
twitter: createSocialTransform("twitter"),
|
|
87
|
-
instagram: createSocialTransform("instagram"),
|
|
88
|
-
diaspora: createSocialTransform("diaspora"),
|
|
89
|
-
mastodon: createSocialTransform("mastodon"),
|
|
90
|
-
telegram: createSocialTransform("telegram"),
|
|
91
|
-
signal: createSocialTransform("signal")
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Crée une instance de `User`.
|
|
96
|
-
*
|
|
97
|
-
* @param parent
|
|
98
|
-
* L'ApiClient, une entité parente, ou tout objet exposant `apiClient`.
|
|
99
|
-
* @param {{ id?: string, slug?: string }} data Données initiales (il faut au moins `id` OU `slug`).
|
|
100
|
-
* @param {{
|
|
101
|
-
* EndpointApi: EndpointApiDep,
|
|
102
|
-
* Organization: typeof import("./Organization.js").Organization,
|
|
103
|
-
* Project: typeof import("./Project.js").Project,
|
|
104
|
-
* Event: typeof import("./Event.js").Event,
|
|
105
|
-
* Poi: typeof import("./Poi.js").Poi,
|
|
106
|
-
* Badge: typeof import("./Badge.js").Badge,
|
|
107
|
-
* News: typeof import("./News.js").News
|
|
108
|
-
* }} deps
|
|
109
|
-
* @throws {ApiError} Si `EndpointApi` n'est pas injecté, ou si `id`/`slug` manquent,
|
|
110
|
-
* ou si une des classes dépendantes n'est pas injectée.
|
|
111
|
-
*/
|
|
112
|
-
constructor(
|
|
113
|
-
parent: ApiClient | any,
|
|
114
|
-
data: { id?: string; slug?: string },
|
|
115
|
-
deps: {
|
|
116
|
-
EndpointApi: any;
|
|
117
|
-
Organization: typeof import("./Organization.js").Organization;
|
|
118
|
-
Project: typeof import("./Project.js").Project;
|
|
119
|
-
Event: typeof import("./Event.js").Event;
|
|
120
|
-
Poi: typeof import("./Poi.js").Poi;
|
|
121
|
-
Badge: typeof import("./Badge.js").Badge;
|
|
122
|
-
News: typeof import("./News.js").News;
|
|
123
|
-
Comment: typeof import("./Comment.js").Comment;
|
|
124
|
-
Answer: typeof import("./Answer.js").Answer;
|
|
125
|
-
Form?: typeof import("./Form.js").Form;
|
|
126
|
-
Classified?: typeof import("./Classified.js").Classified;
|
|
127
|
-
Action?: typeof import("./Action.js").Action;
|
|
128
|
-
}
|
|
129
|
-
) {
|
|
130
|
-
if(!deps.EndpointApi){
|
|
131
|
-
throw new ApiError("EndpointApi class must be injected to avoid circular dependency.", 500);
|
|
132
|
-
}
|
|
133
|
-
if (!data?.id && !data?.slug) {
|
|
134
|
-
throw new ApiError("Vous devez fournir un id ou un slug pour créer un User.", 400);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (!deps.Organization) throw new ApiError("Organization class must be injected.", 500);
|
|
138
|
-
if (!deps.Project) throw new ApiError("Project class must be injected.", 500);
|
|
139
|
-
if (!deps.Event) throw new ApiError("Event class must be injected.", 500);
|
|
140
|
-
if (!deps.Poi) throw new ApiError("Poi class must be injected.", 500);
|
|
141
|
-
if (!deps.Badge) throw new ApiError("Badge class must be injected.", 500);
|
|
142
|
-
if (!deps.News) throw new ApiError("News class must be injected.", 500);
|
|
143
|
-
if (!deps.Answer) throw new ApiError("Answer class must be injected.", 500);
|
|
144
|
-
|
|
145
|
-
super(parent, data, deps);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
override get slug() {
|
|
149
|
-
return this._draftData.slug || null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
override get isMe() {
|
|
153
|
-
return this.isConnected && this.userId === this.id;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
get parentIsMe() {
|
|
157
|
-
return super.isMe;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
get isActingUser() {
|
|
161
|
-
return this.parentIsMe && !this.isMe;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** @returns {"citoyens"} */
|
|
165
|
-
override getEntityType(): "citoyens" { return "citoyens"; }
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Récupère le profil complet de l'utilisateur.
|
|
169
|
-
* Si l'utilisateur est connecté, on appelle le endpoint ME_INFO_URL,
|
|
170
|
-
* sinon, on peut imaginer appeler un endpoint public.
|
|
171
|
-
*
|
|
172
|
-
* @returns Le profil complet.
|
|
173
|
-
*/
|
|
174
|
-
override async get(): Promise<Record<string, any>> {
|
|
175
|
-
// Tombstone : `meInfoUrl()` est basé sur le token (ignore `this.id`), donc cette
|
|
176
|
-
// lecture ne lèverait pas via le garde `!this.id` après un delete — on garde ici.
|
|
177
|
-
this._checkNotDeleted();
|
|
178
|
-
return this.apiClient.safeCall(async () => {
|
|
179
|
-
if (this.isMe) {
|
|
180
|
-
const data = await this.endpointApi.meInfoUrl();
|
|
181
|
-
this._setData(data as UserItemNormalized, { forceInitialDraftReset: true });
|
|
182
|
-
return data;
|
|
183
|
-
} else {
|
|
184
|
-
const data = await this._getPublicProfile();
|
|
185
|
-
this._setData(data as UserItemNormalized, { forceInitialDraftReset: true });
|
|
186
|
-
return data;
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Changer le mot de passe : Permet de changer le mot de passe d'un utilisateur.
|
|
193
|
-
* Constant : CHANGE_PASSWORD
|
|
194
|
-
* @param data - Données envoyées à l'API
|
|
195
|
-
* @returns - Les données de réponse.
|
|
196
|
-
* @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse.
|
|
197
|
-
* @throws {ApiAuthenticationError} - En cas d'erreur d'authentification.
|
|
198
|
-
* @throws {Error} - En cas d'erreur inattendue.
|
|
199
|
-
*/
|
|
200
|
-
async changePassword(data: ChangePasswordData): Promise<unknown> {
|
|
201
|
-
return this.callIsMe(() => this.endpointApi.changePassword(data));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Supprimer un compte : Permet de supprimer un compte utilisateur.
|
|
206
|
-
* Constant : DELETE_ACCOUNT
|
|
207
|
-
* @param data - Données envoyées à l'API
|
|
208
|
-
* @returns - Les données de réponse.
|
|
209
|
-
* @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse.
|
|
210
|
-
* @throws {ApiAuthenticationError} - En cas d'erreur d'authentification.
|
|
211
|
-
* @throws {Error} - En cas d'erreur inattendue.
|
|
212
|
-
*/
|
|
213
|
-
async delete(data: DeleteAccountData): Promise<unknown> {
|
|
214
|
-
const result = await this.callIsMe(() => this.endpointApi.deleteAccount(data));
|
|
215
|
-
|
|
216
|
-
// Vider les objets réactifs sans casser la réactivité (cf. Action/Answer/News/Comment.delete)
|
|
217
|
-
Object.keys(this._draftData).forEach(key => delete this._draftData[key]);
|
|
218
|
-
Object.keys(this._serverData).forEach(key => delete (this._serverData as any)[key]);
|
|
219
|
-
|
|
220
|
-
// Marquer comme supprimé → toute opération ultérieure lève le tombstone.
|
|
221
|
-
this._isDeleted = true;
|
|
222
|
-
return result;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Sauvegarde les modifications de l'utilisateur en appelant les endpoints correspondants.
|
|
227
|
-
* Seul l'utilisateur connecté peut se modifier lui-même.
|
|
228
|
-
*
|
|
229
|
-
* @returns - Données serveur mises à jour si applicable.
|
|
230
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas autorisé.
|
|
231
|
-
*/
|
|
232
|
-
override async save(): Promise<Record<string, any>> {
|
|
233
|
-
if(!this.isMe){
|
|
234
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour sauvegarder.", 401);
|
|
235
|
-
}
|
|
236
|
-
return await super.save();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
override _add = async () => {
|
|
240
|
-
throw new ApiError("Vous ne pouvez pas ajouter un utilisateur par ce moyen.", 403);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Met à jour les blocs modifiés de l'utilisateur via les constantes de schéma définies.
|
|
245
|
-
*
|
|
246
|
-
* @param payload - Données courantes à comparer et envoyer.
|
|
247
|
-
* @returns - Indique s'il y a eu une modification réelle.
|
|
248
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas autorisé à effectuer cette action.
|
|
249
|
-
*/
|
|
250
|
-
override _update = async (payload: Record<string, any>): Promise<boolean> => {
|
|
251
|
-
if (!this._calledFromSave) {
|
|
252
|
-
throw new ApiError("utilisation invalide de _update, utilisez save", 400);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (payload.id) {
|
|
256
|
-
delete payload.id;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
let hasChanged = false;
|
|
260
|
-
|
|
261
|
-
// Sinon, on fait les updates en blocs
|
|
262
|
-
for (const [constant, methodName] of Array.from(User.UPDATE_BLOCKS)) {
|
|
263
|
-
const blockData = this._extractChangedFieldsFromSchema(
|
|
264
|
-
this.apiClient,
|
|
265
|
-
constant,
|
|
266
|
-
{ ...payload, ...this.defaultFields},
|
|
267
|
-
() => this.initialDraftData,
|
|
268
|
-
this.removeFields
|
|
269
|
-
);
|
|
270
|
-
if (blockData && Object.keys(blockData).length > 0) {
|
|
271
|
-
await this._invokeBlockMethod(User.UPDATE_BLOCKS, methodName, blockData);
|
|
272
|
-
hasChanged = true;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return hasChanged;
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
static override fromServerData(data: any, parent: any, deps: any): User {
|
|
280
|
-
const Ctor = this as any;
|
|
281
|
-
const instance = new Ctor(parent, data, deps);
|
|
282
|
-
if (data && typeof data === "object" && Object.keys(data).length > 0 && typeof instance._setData === "function") {
|
|
283
|
-
// Voir BaseEntity.fromServerData : on réconcilie le baseline de diff avec l'état serveur
|
|
284
|
-
// pour qu'une entité fraîchement revifiée ne se croie pas « entièrement modifiée ».
|
|
285
|
-
instance._setData(data as UserItemNormalized, { forceInitialDraftReset: true });
|
|
286
|
-
}
|
|
287
|
-
return instance;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* {@inheritDoc BaseEntity#updateSettings}
|
|
292
|
-
*
|
|
293
|
-
* Mettre à jour les paramètres utilisateur : Mise à jour des paramètres spécifiques d'un utilisateur.
|
|
294
|
-
*/
|
|
295
|
-
override async updateSettings(data: Parameters<BaseEntity<UserItemNormalized>["updateSettings"]>[0]) {
|
|
296
|
-
if(!this.isMe){
|
|
297
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour les paramètres.", 401);
|
|
298
|
-
}
|
|
299
|
-
const result = await super.updateSettings(data);
|
|
300
|
-
await this.refresh();
|
|
301
|
-
return result;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* {@inheritDoc BaseEntity#updateDescription}
|
|
306
|
-
*
|
|
307
|
-
* Mettre à jour la description d'un élément : Permet de mettre à jour la description courte et complète d'un élément.
|
|
308
|
-
*/
|
|
309
|
-
override async updateDescription(data: Parameters<BaseEntity<UserItemNormalized>["updateDescription"]>[0]) {
|
|
310
|
-
if(!this.isMe){
|
|
311
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour la description.", 401);
|
|
312
|
-
}
|
|
313
|
-
return super.updateDescription(data);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* {@inheritDoc BaseEntity#updateInfo}
|
|
318
|
-
*
|
|
319
|
-
* Mettre à jour les informations d'un élément : Permet de mettre à jour les informations générales d'un élément (nom, contacts, etc.).
|
|
320
|
-
*/
|
|
321
|
-
override async updateInfo(data: Parameters<BaseEntity<UserItemNormalized>["updateInfo"]>[0]) {
|
|
322
|
-
if(!this.isMe){
|
|
323
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour les informations.", 401);
|
|
324
|
-
}
|
|
325
|
-
return super.updateInfo(data);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* {@inheritDoc BaseEntity#updateSocial}
|
|
330
|
-
*
|
|
331
|
-
* Mettre à jour les réseaux sociaux d'un élément : Permet de mettre à jour les liens vers les réseaux sociaux d'un élément.
|
|
332
|
-
*/
|
|
333
|
-
override async updateSocial(data: Parameters<BaseEntity<UserItemNormalized>["updateSocial"]>[0]) {
|
|
334
|
-
if(!this.isMe){
|
|
335
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour les réseaux sociaux.", 401);
|
|
336
|
-
}
|
|
337
|
-
return super.updateSocial(data);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* {@inheritDoc BaseEntity#updateLocality}
|
|
342
|
-
*
|
|
343
|
-
* Mettre à jour les localités d'un élément : Permet de mettre à jour l'adresse et les informations géographiques d'un élément.
|
|
344
|
-
*/
|
|
345
|
-
override async updateLocality(data: Parameters<BaseEntity<UserItemNormalized>["updateLocality"]>[0]) {
|
|
346
|
-
if(!this.isMe){
|
|
347
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour la localité.", 401);
|
|
348
|
-
}
|
|
349
|
-
return super.updateLocality(data);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* {@inheritDoc BaseEntity#updateSlug}
|
|
354
|
-
*
|
|
355
|
-
* Mettre à jour le slug d'un élément : Permet de mettre à jour le slug pour une URL simplifiée.
|
|
356
|
-
*/
|
|
357
|
-
override async updateSlug(data: Parameters<BaseEntity<UserItemNormalized>["updateSlug"]>[0]) {
|
|
358
|
-
if(!this.isMe){
|
|
359
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour le slug.", 401);
|
|
360
|
-
}
|
|
361
|
-
return super.updateSlug(data);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* {@inheritDoc BaseEntity#updateImageProfil}
|
|
366
|
-
*
|
|
367
|
-
* Mettre à jour l'image de profil : Permet de mettre à jour l'image de profil d'un utilisateur ou d'une entité.
|
|
368
|
-
*/
|
|
369
|
-
override async updateImageProfil(data: Parameters<BaseEntity<UserItemNormalized>["updateImageProfil"]>[0]) {
|
|
370
|
-
if(!this.isMe){
|
|
371
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour l'image de profil.", 401);
|
|
372
|
-
}
|
|
373
|
-
return super.updateImageProfil(data);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* {@inheritDoc BaseEntity#updateImageBanner}
|
|
379
|
-
*
|
|
380
|
-
* Mettre à jour l'image de bannière : Permet de mettre à jour l'image de bannière d'un utilisateur ou d'une entité.
|
|
381
|
-
*/
|
|
382
|
-
override async updateImageBanner(data: Parameters<BaseEntity<UserItemNormalized>["updateImageBanner"]>[0]) {
|
|
383
|
-
if(!this.isMe){
|
|
384
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour mettre à jour l'image de bannière.", 401);
|
|
385
|
-
}
|
|
386
|
-
return super.updateImageBanner(data);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* {@inheritDoc BaseEntity#getOrganizations}
|
|
392
|
-
* @param [data]
|
|
393
|
-
*
|
|
394
|
-
* Récupérer les organisations d'un utilisateur : Récupère la liste des organisations auxquelles l'utilisateur appartient.
|
|
395
|
-
* Constant : GET_ORGANIZATIONS_ADMIN | GET_ORGANIZATIONS_NO_ADMIN
|
|
396
|
-
*/
|
|
397
|
-
override async getOrganizations(
|
|
398
|
-
data: Partial<GetOrganizationsAdminData | GetOrganizationsNoAdminData> = {},
|
|
399
|
-
options?: { restoredState?: PaginatorState }
|
|
400
|
-
) {
|
|
401
|
-
data.searchType = this._getDefaultFromEndpoint("GET_ORGANIZATIONS_NO_ADMIN", "searchType") as GetOrganizationsNoAdminData["searchType"];
|
|
402
|
-
// data.searchBy = "ALL";
|
|
403
|
-
|
|
404
|
-
const paginator = this._createPaginatorEngine({
|
|
405
|
-
initialData: data,
|
|
406
|
-
methodName: "getOrganizations",
|
|
407
|
-
restoredState: options?.restoredState,
|
|
408
|
-
finalizer: async (finalData) => {
|
|
409
|
-
delete finalData?.pathParams;
|
|
410
|
-
|
|
411
|
-
const fetchFn = this.isMe
|
|
412
|
-
? () => this.callIsMe(() => this.endpointApi.getOrganizationsAdmin(finalData))
|
|
413
|
-
: () => this.endpointApi.getOrganizationsNoAdmin(finalData);
|
|
414
|
-
|
|
415
|
-
if (!this.isMe && !finalData.filters) {
|
|
416
|
-
finalData.filters = {
|
|
417
|
-
[`links.members.${this.id}`]: { "$exists": true },
|
|
418
|
-
[`links.members.${this.id}.toBeValidated`]: { "$exists": false },
|
|
419
|
-
[`links.members.${this.id}.isInviting`]: { "$exists": false }
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return fetchFn();
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
return paginator.next() as Promise<PaginatorPage<Organization>>;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Récupère les lieux (organizations) memberOf de l'utilisateur, filtrés
|
|
432
|
-
* côté serveur par des filters arbitraires (tags, source.key, etc.) et
|
|
433
|
-
* un flag `notSourceKey` configurables.
|
|
434
|
-
*
|
|
435
|
-
* Mirroir de `getOrganizations` mais permet de passer dynamiquement les
|
|
436
|
-
* filtres du finder du formulaire (vue collaborative coform/place), pour
|
|
437
|
-
* que la pagination soit correcte (filtrage côté serveur, pas côté client).
|
|
438
|
-
*
|
|
439
|
-
* Constant : GET_USER_ELIGIBLE_PLACES
|
|
440
|
-
*/
|
|
441
|
-
async getEligiblePlaces(
|
|
442
|
-
data: Partial<GetUserEligiblePlacesData> = {},
|
|
443
|
-
options?: { restoredState?: PaginatorState }
|
|
444
|
-
) {
|
|
445
|
-
// Force le default `searchType` ([NGO, Cooperative, ...]) — le PHP ne
|
|
446
|
-
// retourne rien si le tableau est vide / absent. Pattern aligné avec
|
|
447
|
-
// `getOrganizations`.
|
|
448
|
-
data.searchType = this._getDefaultFromEndpoint(
|
|
449
|
-
"GET_USER_ELIGIBLE_PLACES",
|
|
450
|
-
"searchType"
|
|
451
|
-
) as GetUserEligiblePlacesData["searchType"];
|
|
452
|
-
|
|
453
|
-
const paginator = this._createPaginatorEngine({
|
|
454
|
-
initialData: data,
|
|
455
|
-
methodName: "getEligiblePlaces",
|
|
456
|
-
restoredState: options?.restoredState,
|
|
457
|
-
finalizer: async (finalData) => {
|
|
458
|
-
delete finalData?.pathParams;
|
|
459
|
-
|
|
460
|
-
// Auto-merge du filtre memberOf par défaut si l'appelant n'en pousse
|
|
461
|
-
// que des filters "métier" (tags, etc.). On garde la sémantique
|
|
462
|
-
// "uniquement les orgs où l'user est membre validé".
|
|
463
|
-
const userFilters = finalData.filters ?? {};
|
|
464
|
-
const memberOfDefaults: Record<string, unknown> = {
|
|
465
|
-
[`links.members.${this.id}`]: { "$exists": true },
|
|
466
|
-
[`links.members.${this.id}.toBeValidated`]: { "$exists": false },
|
|
467
|
-
[`links.members.${this.id}.isInviting`]: { "$exists": false },
|
|
468
|
-
};
|
|
469
|
-
finalData.filters = { ...memberOfDefaults, ...userFilters };
|
|
470
|
-
|
|
471
|
-
// Cast nécessaire : callIsMe est typé Promise<unknown>, mais l'endpoint
|
|
472
|
-
// retourne bien la shape `{ results, count }` attendue par le finalizer.
|
|
473
|
-
// Pattern aligné avec getOrganizations (cf. la branche ternaire au-dessus).
|
|
474
|
-
return this.callIsMe(() => this.endpointApi.getUserEligiblePlaces(finalData)) as ReturnType<
|
|
475
|
-
typeof this.endpointApi.getUserEligiblePlaces
|
|
476
|
-
>;
|
|
477
|
-
},
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
return paginator.next() as Promise<PaginatorPage<Organization>>;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* {@inheritDoc BaseEntity#getProjects}
|
|
485
|
-
*
|
|
486
|
-
* Récupérer les projets d'un utilisateur : Récupère la liste des projets auxquels l'utilisateur contribue.
|
|
487
|
-
*/
|
|
488
|
-
override async getProjects(data: Parameters<BaseEntity<UserItemNormalized>["getProjects"]>[0] = {}) {
|
|
489
|
-
return super.getProjects(data);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* {@inheritDoc BaseEntity#getEvents}
|
|
494
|
-
*
|
|
495
|
-
* Récupère les événements d'un utilisateur.
|
|
496
|
-
*/
|
|
497
|
-
override async getEvents(data: Parameters<BaseEntity<UserItemNormalized>["getEvents"]>[0] = {}) {
|
|
498
|
-
return super.getEvents(data);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* {@inheritDoc BaseEntity#getPois}
|
|
503
|
-
*
|
|
504
|
-
* Récupérer les POIs
|
|
505
|
-
*/
|
|
506
|
-
override async getPois(data: Parameters<BaseEntity<UserItemNormalized>["getPois"]>[0] = {}) {
|
|
507
|
-
return super.getPois(data);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* {@inheritDoc BaseEntity#getNews}
|
|
512
|
-
*
|
|
513
|
-
* Récupérer les actualités : Récupère la liste des actualités liées à l'utilisateur.
|
|
514
|
-
*/
|
|
515
|
-
override async getNews(data: Parameters<BaseEntity<UserItemNormalized>["getNews"]>[0] = {}) {
|
|
516
|
-
return super.getNews(data);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Récupérer les amis administrables : Récupère les amis administrée par l’utilisateur.
|
|
521
|
-
* Constant : GET_FRIENDS_ADMIN
|
|
522
|
-
* question : qui peut voir la liste d'amis, seulement l'utilisateur connecté ? ou tous les utilisateurs connectés ?
|
|
523
|
-
* actuellement, c'est tous les utilisateurs connectés
|
|
524
|
-
*/
|
|
525
|
-
async getFriends(
|
|
526
|
-
data: Partial<GetFriendsAdminData> = {},
|
|
527
|
-
options?: { restoredState?: PaginatorState }
|
|
528
|
-
): Promise<PaginatorPage<User>> {
|
|
529
|
-
data.searchType = this._getDefaultFromEndpoint("GET_FRIENDS_ADMIN", "searchType") as GetFriendsAdminData["searchType"];
|
|
530
|
-
// data.searchBy = "ALL";
|
|
531
|
-
|
|
532
|
-
const paginator = this._createPaginatorEngine({
|
|
533
|
-
initialData: data,
|
|
534
|
-
methodName: "getFriends",
|
|
535
|
-
restoredState: options?.restoredState,
|
|
536
|
-
finalizer: async (finalData) => {
|
|
537
|
-
|
|
538
|
-
delete finalData?.pathParams;
|
|
539
|
-
|
|
540
|
-
if (!this.isMe && this.id){
|
|
541
|
-
// is not me add id
|
|
542
|
-
finalData.pathParams = { id: this.id };
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
return this.endpointApi.getFriendsAdmin(finalData);
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
return paginator.next() as Promise<PaginatorPage<User>>;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Récupérer les suivis
|
|
554
|
-
* Constant : GET_SUBSCRIPTIONS / GET_SUBSCRIPTIONS_ADMIN
|
|
555
|
-
*/
|
|
556
|
-
async getSubscriptions(
|
|
557
|
-
data: Partial<GetSubscriptionsAdminData | GetSubscriptionsData> = {},
|
|
558
|
-
options?: { restoredState?: PaginatorState }
|
|
559
|
-
): Promise<PaginatorPage<BaseEntity<any>>> {
|
|
560
|
-
data.searchType = this._getDefaultFromEndpoint("GET_SUBSCRIPTIONS", "searchType") as GetSubscriptionsAdminData["searchType"];
|
|
561
|
-
// data.searchBy = "ALL";
|
|
562
|
-
|
|
563
|
-
const paginator = this._createPaginatorEngine({
|
|
564
|
-
initialData: data,
|
|
565
|
-
methodName: "getSubscriptions",
|
|
566
|
-
restoredState: options?.restoredState,
|
|
567
|
-
finalizer: async (finalData) => {
|
|
568
|
-
|
|
569
|
-
delete finalData?.pathParams;
|
|
570
|
-
|
|
571
|
-
const fetchFn = this.isMe
|
|
572
|
-
? () => this.callIsMe(() => this.endpointApi.getSubscriptionsAdmin(finalData))
|
|
573
|
-
: () => this.endpointApi.getSubscriptions(finalData);
|
|
574
|
-
|
|
575
|
-
if (!this.isMe && !finalData.filters) {
|
|
576
|
-
finalData.filters = {
|
|
577
|
-
[`links.followers.${this.id}`]: { "$exists": true },
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return fetchFn();
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
return paginator.next() as Promise<PaginatorPage<BaseEntity<any>>>;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* {@inheritDoc BaseEntity#getSubscribers}
|
|
590
|
-
*
|
|
591
|
-
* Récupérer les abonnés
|
|
592
|
-
*/
|
|
593
|
-
override async getSubscribers(data: Parameters<BaseEntity<UserItemNormalized>["getSubscribers"]>[0] = {}) {
|
|
594
|
-
return super.getSubscribers(data);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Obtenir la liste des événements
|
|
599
|
-
* Constant : GET_EVENTS
|
|
600
|
-
* TODO : il n'est pas fait encore pour GET_EVENTS_ADMIN / GET_EVENTS_NO_ADMIN comme les autres dans endpointApi
|
|
601
|
-
*/
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* {@inheritDoc BaseEntity#getBadgesIssuer}
|
|
605
|
-
*
|
|
606
|
-
* Liste des badges créés par l'utilisateur
|
|
607
|
-
*/
|
|
608
|
-
override async getBadgesIssuer(data: Parameters<BaseEntity<UserItemNormalized>["getBadgesIssuer"]>[0] = {}) {
|
|
609
|
-
return super.getBadgesIssuer(data);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Liste des badges associés à l'utilisateur
|
|
614
|
-
*
|
|
615
|
-
* TODO : documenté le fonctionnement et sont utilisation avec un exemple
|
|
616
|
-
*/
|
|
617
|
-
async getBadges(filter: Record<string, any> = {}): Promise<Badge[]> {
|
|
618
|
-
/*
|
|
619
|
-
"badges": {
|
|
620
|
-
"627df5663a0fae60e57f4a31": {
|
|
621
|
-
"attenteRecepteur": true,
|
|
622
|
-
"expiredOn": false,
|
|
623
|
-
"isParcours": "false",
|
|
624
|
-
"issuedOn": "2023-10-02T14:00:00.000Z",
|
|
625
|
-
"name": "CODEV",
|
|
626
|
-
"show": "true",
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
|
-
*/
|
|
630
|
-
|
|
631
|
-
const filteredBadges = await this.linkEntitiesFromServerData("badges", filter);
|
|
632
|
-
// voir si je les met dans objet { count, results }
|
|
633
|
-
return filteredBadges;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* {@inheritDoc BaseEntity#getGallery}
|
|
638
|
-
*
|
|
639
|
-
* Récupérer la galerie de l'utilisateur
|
|
640
|
-
*/
|
|
641
|
-
override async getGallery(data: Parameters<BaseEntity<UserItemNormalized>["getGallery"]>[0] = {}) {
|
|
642
|
-
return super.getGallery(data);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
async user(userData: { id?: string, slug?: string }): Promise<User> {
|
|
646
|
-
if(!this.isMe){
|
|
647
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur", 401);
|
|
648
|
-
}
|
|
649
|
-
if (!userData.id && !userData.slug) {
|
|
650
|
-
throw new ApiError("Vous devez fournir un id ou un slug pour créer un User.", 400);
|
|
651
|
-
}
|
|
652
|
-
const requiredDeps = this.deps as any;
|
|
653
|
-
const user = new User(this, userData, requiredDeps as any);
|
|
654
|
-
await user.get();
|
|
655
|
-
return user;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* {@inheritDoc BaseEntity#organization}
|
|
660
|
-
*
|
|
661
|
-
* Crée une instance d'organisation et récupère son profil si nécessaire.
|
|
662
|
-
*/
|
|
663
|
-
override async organization(organizationData: Parameters<BaseEntity<UserItemNormalized>["organization"]>[0] = {}) {
|
|
664
|
-
if(!("id" in organizationData) && !("slug" in organizationData) && !this.isMe){
|
|
665
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer une organisation.", 401);
|
|
666
|
-
}
|
|
667
|
-
return super.organization(organizationData);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/**
|
|
671
|
-
* {@inheritDoc BaseEntity#project}
|
|
672
|
-
*
|
|
673
|
-
* Crée une instance de projet et récupère son profil si nécessaire.
|
|
674
|
-
*/
|
|
675
|
-
override async project(projectData: Parameters<BaseEntity<UserItemNormalized>["project"]>[0] = {}) {
|
|
676
|
-
if(!("id" in projectData) && !("slug" in projectData) && !this.isMe){
|
|
677
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer un projet.", 401);
|
|
678
|
-
}
|
|
679
|
-
return super.project(projectData);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Bloqué sur User : un Form nécessite un costumContext (slug d'Organization/Project)
|
|
684
|
-
* que User n'a pas. Utilisez `user.organization({slug}).then(o => o.form({id}))`
|
|
685
|
-
* ou `user.project({slug}).then(p => p.form({id}))` pour la chaîne propagée.
|
|
686
|
-
*/
|
|
687
|
-
override async form(): Promise<never> {
|
|
688
|
-
throw new ApiError(`form n'existe pas dans ${this.constructor.name}`, 501);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* {@inheritDoc BaseEntity#news}
|
|
693
|
-
*
|
|
694
|
-
* Crée une instance de news et la récupère si nécessaire.
|
|
695
|
-
*/
|
|
696
|
-
override async news(newsData: Parameters<BaseEntity<UserItemNormalized>["news"]>[0] = {}) {
|
|
697
|
-
if(!("id" in newsData) && !this.isMe){
|
|
698
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer une actualité.", 401);
|
|
699
|
-
}
|
|
700
|
-
return super.news(newsData);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* {@inheritDoc BaseEntity#poi}
|
|
705
|
-
*
|
|
706
|
-
* Crée une instance de POI et la récupère si nécessaire.
|
|
707
|
-
*/
|
|
708
|
-
override async poi(poiData: Parameters<BaseEntity<UserItemNormalized>["poi"]>[0] = {}) {
|
|
709
|
-
if(!("id" in poiData) && !("slug" in poiData) && !this.isMe){
|
|
710
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer un POI.", 401);
|
|
711
|
-
}
|
|
712
|
-
return super.poi(poiData);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* {@inheritDoc BaseEntity#event}
|
|
717
|
-
*
|
|
718
|
-
* Crée une instance d'événement et la récupère si nécessaire.
|
|
719
|
-
*/
|
|
720
|
-
override async event(eventData: Parameters<BaseEntity<UserItemNormalized>["event"]>[0] = {}) {
|
|
721
|
-
if(!("id" in eventData) && !("slug" in eventData) && !this.isMe){
|
|
722
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer un événement.", 401);
|
|
723
|
-
}
|
|
724
|
-
return super.event(eventData);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* Ouvre un *scope costum* : les entités créées via le scope (`poi`/`organization`/`project`/`event`)
|
|
729
|
-
* portent le contexte costum (costumSlug + champs costum validés par le binding du costum à `element/save`).
|
|
730
|
-
*
|
|
731
|
-
* @example
|
|
732
|
-
* const tl = await me.costum("reseauTierslieux");
|
|
733
|
-
* const org = await tl.organization({ name: "Mon TL", manageModel: "Association", buildingSurfaceArea: "120" });
|
|
734
|
-
* await org.save();
|
|
735
|
-
*
|
|
736
|
-
* Deux formes :
|
|
737
|
-
* - `me.costum(slug)` : slug d'un costum du registry (autocomplété) OU n'importe quel slug (`string`) —
|
|
738
|
-
* le backend reste l'autorité de validité (`found`). Slug hors registry sans champ → inc1b (résolution live).
|
|
739
|
-
* - `me.costum(entity)` : ouvre un scope FIELDLESS depuis une entité DÉJÀ CHARGÉE (org/projet/event qui porte
|
|
740
|
-
* un costum), zéro fetch — `source.key` = slug de l'entité. Cas « créer sous ce site nu ».
|
|
741
|
-
*
|
|
742
|
-
* @param arg - slug (`KnownCostumSlug | string`) OU une entité chargée porteuse d'un costum.
|
|
743
|
-
*/
|
|
744
|
-
async costum(slug: KnownCostumSlug | (string & {})): Promise<CostumScope>;
|
|
745
|
-
async costum(entity: BaseEntity): Promise<CostumScope>;
|
|
746
|
-
async costum(arg: KnownCostumSlug | (string & {}) | BaseEntity): Promise<CostumScope> {
|
|
747
|
-
if (typeof arg === "string") return resolveCostumScope(this, arg);
|
|
748
|
-
return costumScopeFromEntity(this, arg);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
/**
|
|
752
|
-
* {@inheritDoc BaseEntity#badge}
|
|
753
|
-
*
|
|
754
|
-
* Crée une instance de badge et la récupère si nécessaire.
|
|
755
|
-
*/
|
|
756
|
-
override async badge(badgeData: Parameters<BaseEntity<UserItemNormalized>["badge"]>[0] = {}) {
|
|
757
|
-
if(!("id" in badgeData) && !this.isMe){
|
|
758
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer un badge.", 401);
|
|
759
|
-
}
|
|
760
|
-
return super.badge(badgeData);
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
/**
|
|
764
|
-
* ───────────────────────────────
|
|
765
|
-
* Lien utilisateur ↔ utilisateur (ami)
|
|
766
|
-
* (demander, valider, se retirer)
|
|
767
|
-
* ───────────────────────────────
|
|
768
|
-
*/
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* Envoie une demande d'amitié à cet utilisateur.
|
|
772
|
-
* L'utilisateur ciblé devra valider la demande pour établir la relation.
|
|
773
|
-
*
|
|
774
|
-
* @returns - Résultat de la requête.
|
|
775
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
|
|
776
|
-
*/
|
|
777
|
-
async sendFriendRequest(): Promise<unknown> {
|
|
778
|
-
if (!this.isActingUser || !this.userId) {
|
|
779
|
-
throw new ApiError("Vous devez être connecté pour envoyer une demande d'amis.", 401);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
this._checkLinkableEntity();
|
|
783
|
-
if (!this.id) {
|
|
784
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const { connectTypeConnect } = this._getLinkMeta();
|
|
788
|
-
const userLink = this._getLinkFromConnectedUser();
|
|
789
|
-
|
|
790
|
-
if (!userLink) {
|
|
791
|
-
const data: ConnectData = {
|
|
792
|
-
childId: this.userId,
|
|
793
|
-
childType: "citoyens",
|
|
794
|
-
parentType: this.getEntityType(),
|
|
795
|
-
parentId: this.id,
|
|
796
|
-
connectType: connectTypeConnect
|
|
797
|
-
};
|
|
798
|
-
const retour = await this.endpointApi.connect(data);
|
|
799
|
-
// TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
800
|
-
await this.userContext?.refresh();
|
|
801
|
-
return retour;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
if (userLink.isInviting && userLink.invitorId === this.id) {
|
|
805
|
-
return this.acceptFriendRequest();
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
if (userLink.isInviting && userLink.invitorId === this.userId) {
|
|
809
|
-
throw new ApiError("Vous avez déjà envoyé une demande d'amis à cet utilisateur.", 403);
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
throw new ApiError("Vous êtes déjà connecté à cette entité.", 409);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/**
|
|
816
|
-
* Accepte une demande d'amitié envoyée par cet utilisateur.
|
|
817
|
-
* Cette action établit un lien entre les deux utilisateurs.
|
|
818
|
-
*
|
|
819
|
-
* @returns - Résultat de la validation du lien.
|
|
820
|
-
* @throws {ApiError} - Si aucune invitation n'est en attente ou si l'action est interdite.
|
|
821
|
-
*/
|
|
822
|
-
async acceptFriendRequest(): Promise<unknown> {
|
|
823
|
-
if (!this.isActingUser || !this.userId) {
|
|
824
|
-
throw new ApiError("Vous devez être connecté pour accepter une demande d'amitié.", 401);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
this._checkLinkableEntity();
|
|
828
|
-
if (!this.id) {
|
|
829
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
const userLink = this._getLinkFromConnectedUser();
|
|
833
|
-
|
|
834
|
-
if (userLink?.isInviting && userLink.invitorId === this.id) {
|
|
835
|
-
const data: LinkValidateData = {
|
|
836
|
-
childId: this.userId,
|
|
837
|
-
childType: "citoyens",
|
|
838
|
-
parentType: this.getEntityType(),
|
|
839
|
-
parentId: this.id,
|
|
840
|
-
linkOption: "isInviting"
|
|
841
|
-
};
|
|
842
|
-
const retour = await this.endpointApi.linkValidate(data);
|
|
843
|
-
// TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
844
|
-
await this.userContext?.refresh();
|
|
845
|
-
return retour;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
throw new ApiError("Vous n'avez pas d'invitation à valider.", 404);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Supprime la relation d'amitié avec cet utilisateur.
|
|
853
|
-
* Cette action annule tout lien existant entre les deux profils.
|
|
854
|
-
*
|
|
855
|
-
* @returns - Résultat de la suppression.
|
|
856
|
-
* @throws {ApiError} - Si aucune relation n'existe.
|
|
857
|
-
*/
|
|
858
|
-
async removeFriend(): Promise<unknown> {
|
|
859
|
-
if (!this.isActingUser || !this.userId) {
|
|
860
|
-
throw new ApiError("Vous devez être connecté pour supprimer un ami.", 401);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
this._checkLinkableEntity();
|
|
864
|
-
if (!this.id) {
|
|
865
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
const { connectTypeDisconnect } = this._getLinkMeta();
|
|
869
|
-
const userLink = this._getLinkFromConnectedUser();
|
|
870
|
-
|
|
871
|
-
if (!userLink) {
|
|
872
|
-
throw new ApiError("Vous n'êtes pas connecté à cette entité.", 404);
|
|
873
|
-
}
|
|
874
|
-
const data: DisconnectData = {
|
|
875
|
-
childId: this.userId,
|
|
876
|
-
childType: "citoyens",
|
|
877
|
-
parentType: this.getEntityType(),
|
|
878
|
-
parentId: this.id,
|
|
879
|
-
connectType: connectTypeDisconnect
|
|
880
|
-
};
|
|
881
|
-
const retour = await this.endpointApi.disconnect(data);
|
|
882
|
-
// TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
883
|
-
await this.userContext?.refresh();
|
|
884
|
-
return retour;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
override async requestToJoin(): Promise<never> {
|
|
889
|
-
throw new ApiError("l'utilisation de requestToJoin n'est pas autorisée sur un utilisateur.", 403);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
override async requestToJoinAdmin(): Promise<never> {
|
|
893
|
-
throw new ApiError("l'utilisation de requestToJoinAdmin n'est pas autorisée sur un utilisateur.", 403);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
override async acceptInvitation(): Promise<never> {
|
|
897
|
-
throw new ApiError("l'utilisation de acceptInvitation n'est pas autorisée sur un utilisateur.", 403);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
override async leave(): Promise<never> {
|
|
901
|
-
throw new ApiError("l'utilisation de leave n'est pas autorisée sur un utilisateur.", 403);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
/**
|
|
905
|
-
* Suivre un utilisateur
|
|
906
|
-
* Cette action permet à l'utilisateur connecté de suivre un autre utilisateur.
|
|
907
|
-
* Elle nécessite que l'utilisateur soit connecté.
|
|
908
|
-
*
|
|
909
|
-
* @returns - Résultat de la requête.
|
|
910
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
|
|
911
|
-
*/
|
|
912
|
-
override async follow(): Promise<unknown> {
|
|
913
|
-
if (!this.isActingUser || !this.userId) {
|
|
914
|
-
throw new ApiError("Vous devez être connecté pour suivre un utilisateur.", 401);
|
|
915
|
-
}
|
|
916
|
-
if (!this.id) {
|
|
917
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
const userLink = this.userContext?.serverData?.links?.["follows"]?.[this.id] || null;
|
|
921
|
-
|
|
922
|
-
if (!userLink) {
|
|
923
|
-
const data: FollowData = {
|
|
924
|
-
childId: this.userId,
|
|
925
|
-
childType: "citoyens",
|
|
926
|
-
parentType: this.getEntityType(),
|
|
927
|
-
parentId: this.id
|
|
928
|
-
};
|
|
929
|
-
const retour = await this.endpointApi.follow(data);
|
|
930
|
-
// TODO : reflechier au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
931
|
-
await this.userContext?.refresh();
|
|
932
|
-
return retour;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
throw new ApiError("Vous êtes déjà abonné à cet utilisateur.", 409);
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
/**
|
|
939
|
-
* Se désabonner d'un utilisateur
|
|
940
|
-
* Cette action permet à l'utilisateur connecté de se désabonner d'un autre utilisateur.
|
|
941
|
-
* Elle nécessite que l'utilisateur soit connecté.
|
|
942
|
-
*
|
|
943
|
-
* @returns - Résultat de la requête.
|
|
944
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'action est interdite.
|
|
945
|
-
*/
|
|
946
|
-
override async unfollow(): Promise<unknown> {
|
|
947
|
-
if (!this.isActingUser || !this.userId) {
|
|
948
|
-
throw new ApiError("Vous devez être connecté pour vous désabonner d'un utilisateur.", 401);
|
|
949
|
-
}
|
|
950
|
-
if (!this.id) {
|
|
951
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const userLink = this.userContext?.serverData?.links?.["follows"]?.[this.id] || null;
|
|
955
|
-
|
|
956
|
-
if (userLink) {
|
|
957
|
-
const data: DisconnectData = {
|
|
958
|
-
childId: this.userId,
|
|
959
|
-
childType: "citoyens",
|
|
960
|
-
parentType: this.getEntityType(),
|
|
961
|
-
parentId: this.id,
|
|
962
|
-
connectType: "followers"
|
|
963
|
-
};
|
|
964
|
-
const retour = await this.endpointApi.disconnect(data);
|
|
965
|
-
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
966
|
-
await this.userContext?.refresh();
|
|
967
|
-
return retour;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
throw new ApiError("Vous n'êtes pas abonné à cet utilisateur.", 404);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
/**
|
|
974
|
-
* Vérifie si l'utilisateur connecté est ami avec cet utilisateur.
|
|
975
|
-
* @param options - Options de vérification.
|
|
976
|
-
* @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`.
|
|
977
|
-
* @returns - True si l'utilisateur connecté est ami, sinon false.
|
|
978
|
-
* @throws {ApiError} - Si l'utilisateur n'est pas connecté.
|
|
979
|
-
*/
|
|
980
|
-
isFriend(options?: { silent?: boolean }): boolean {
|
|
981
|
-
const silent = options?.silent ?? true;
|
|
982
|
-
try {
|
|
983
|
-
if (!this.isActingUser) {
|
|
984
|
-
throw new ApiError("Vous devez être connecté pour vérifier si vous êtes ami.", 401);
|
|
985
|
-
}
|
|
986
|
-
} catch (e) {
|
|
987
|
-
if (silent) return false;
|
|
988
|
-
throw e;
|
|
989
|
-
}
|
|
990
|
-
this._assertEntityType("citoyens");
|
|
991
|
-
const userLink = this._getLinkFromConnectedUser();
|
|
992
|
-
if(!userLink) return false;
|
|
993
|
-
return this._validateUserLink(userLink);
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
/**
|
|
997
|
-
* Vérifie si l'utilisateur suit l'entité.
|
|
998
|
-
* @param options - Options de vérification.
|
|
999
|
-
* @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`.
|
|
1000
|
-
* @returns - `true` si l'utilisateur suit l'entité, `false` sinon.
|
|
1001
|
-
* @throws {ApiError}
|
|
1002
|
-
*/
|
|
1003
|
-
override isFollower(options?: { silent?: boolean }): boolean {
|
|
1004
|
-
const silent = options?.silent ?? true;
|
|
1005
|
-
try {
|
|
1006
|
-
if (!this.isActingUser) {
|
|
1007
|
-
throw new ApiError("Vous devez être connecté pour vérifier si il vous suit.", 401);
|
|
1008
|
-
}
|
|
1009
|
-
if (!this.id) {
|
|
1010
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
1011
|
-
}
|
|
1012
|
-
} catch (e) {
|
|
1013
|
-
if (silent) return false;
|
|
1014
|
-
throw e;
|
|
1015
|
-
}
|
|
1016
|
-
this._assertEntityType("citoyens");
|
|
1017
|
-
return this._isLinked("followers");
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* Vérifie si l'utilisateur est abonné à l'entité.
|
|
1022
|
-
* @param options - Options de vérification.
|
|
1023
|
-
* @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`.
|
|
1024
|
-
* @returns - `true` si l'utilisateur est abonné, `false` sinon.
|
|
1025
|
-
* @throws {ApiError}
|
|
1026
|
-
*/
|
|
1027
|
-
override isFollowing(options?: { silent?: boolean }): boolean {
|
|
1028
|
-
const silent = options?.silent ?? true;
|
|
1029
|
-
try {
|
|
1030
|
-
if (!this.isActingUser) {
|
|
1031
|
-
throw new ApiError("Vous devez être connecté pour vérifier si vous le suivez.", 401);
|
|
1032
|
-
}
|
|
1033
|
-
if (!this.id) {
|
|
1034
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
1035
|
-
}
|
|
1036
|
-
} catch (e) {
|
|
1037
|
-
if (silent) return false;
|
|
1038
|
-
throw e;
|
|
1039
|
-
}
|
|
1040
|
-
this._assertEntityType("citoyens");
|
|
1041
|
-
return this._isLinked("follows");
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* Retourne une entité à partir d'un slug.
|
|
1046
|
-
* @param slug - Le slug de l'entité à récupérer.
|
|
1047
|
-
* @returns L'entité correspondante au slug.
|
|
1048
|
-
*/
|
|
1049
|
-
override async entityBySlug(slug: string): Promise<EntityTypes> {
|
|
1050
|
-
if(!this.isMe){
|
|
1051
|
-
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer cet entité.", 401);
|
|
1052
|
-
}
|
|
1053
|
-
return super.entityBySlug(slug);
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
// ────────────────────────────────
|
|
1057
|
-
// Actions sur l'utilisateur par rapport à l'entité parente
|
|
1058
|
-
// ────────────────────────────────
|
|
1059
|
-
|
|
1060
|
-
/**
|
|
1061
|
-
* Valide les préconditions de base (connexion, entité, parent, type).
|
|
1062
|
-
* @private
|
|
1063
|
-
* @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
|
|
1064
|
-
* @param expectedTypes - Types d'entité parent autorisés.
|
|
1065
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1066
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
|
|
1067
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
|
|
1068
|
-
* @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
|
|
1069
|
-
*/
|
|
1070
|
-
private _validateBasePreconditions(methodName: string, expectedTypes: string[]): void {
|
|
1071
|
-
if (!this.userId) {
|
|
1072
|
-
throw new ApiError(`Vous devez être connecté pour ${methodName}.`, 401);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
if (!this.id) {
|
|
1076
|
-
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
if (!this.parent?.id) {
|
|
1080
|
-
throw new ApiError("L'entité parente n'est pas enregistrée.", 404);
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
if (!expectedTypes.includes(this.parent.getEntityType())) {
|
|
1084
|
-
throw new ApiError(`L'entité doit être de type : ${expectedTypes.join(", ")}, reçu : ${this.parent.getEntityType()}`, 400);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
/**
|
|
1089
|
-
* Valide les préconditions pour les méthodes nécessitant d'être membre.
|
|
1090
|
-
* @private
|
|
1091
|
-
* @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
|
|
1092
|
-
* @param expectedTypes - Types d'entité parent autorisés.
|
|
1093
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1094
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
|
|
1095
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
|
|
1096
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
|
|
1097
|
-
* @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
|
|
1098
|
-
*/
|
|
1099
|
-
private _validateMemberPreconditions(methodName: string, expectedTypes: string[]): void {
|
|
1100
|
-
this._validateBasePreconditions(methodName, expectedTypes);
|
|
1101
|
-
|
|
1102
|
-
if (!this.parent?.id) {
|
|
1103
|
-
throw new ApiError("L'entité parente n'est pas enregistrée.", 404);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
// Vérifier que l'utilisateur connecté a le bon rôle selon le type d'entité
|
|
1107
|
-
// Pour les events, tout utilisateur connecté peut agir (pas besoin d'être participant)
|
|
1108
|
-
const parentType = this.parent.getEntityType();
|
|
1109
|
-
|
|
1110
|
-
if (parentType === "events") {
|
|
1111
|
-
// Pour les événements, être connecté suffit (déjà vérifié ci-dessus)
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
let hasRole = false;
|
|
1116
|
-
|
|
1117
|
-
switch (parentType) {
|
|
1118
|
-
case "organizations":
|
|
1119
|
-
hasRole = this.parent.isMember();
|
|
1120
|
-
break;
|
|
1121
|
-
case "projects":
|
|
1122
|
-
hasRole = this.parent.isContributor();
|
|
1123
|
-
break;
|
|
1124
|
-
default:
|
|
1125
|
-
hasRole = false;
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
if (!hasRole) {
|
|
1129
|
-
const roleNames: Record<string, string> = {
|
|
1130
|
-
organizations: "membre",
|
|
1131
|
-
projects: "contributeur"
|
|
1132
|
-
};
|
|
1133
|
-
const roleName = roleNames[parentType] || "membre";
|
|
1134
|
-
throw new ApiError(`Vous devez être ${roleName} pour effectuer cette action.`, 401);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
* Valide les préconditions pour les méthodes nécessitant d'être administrateur.
|
|
1140
|
-
* @private
|
|
1141
|
-
* @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
|
|
1142
|
-
* @param expectedTypes - Types d'entité parent autorisés.
|
|
1143
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1144
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré.
|
|
1145
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée.
|
|
1146
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas membre de l'entité parente.
|
|
1147
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
|
|
1148
|
-
* @throws {ApiError} 400 - Si le type d'entité parent n'est pas valide.
|
|
1149
|
-
*/
|
|
1150
|
-
private _validateAdminPreconditions(methodName: string, expectedTypes: string[]): void {
|
|
1151
|
-
this._validateMemberPreconditions(methodName, expectedTypes);
|
|
1152
|
-
|
|
1153
|
-
if (!this.parent?.isAdmin()) {
|
|
1154
|
-
throw new ApiError("Vous devez être administrateur pour effectuer cette action.", 401);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
/**
|
|
1160
|
-
* Récupère le lien parent pour l'utilisateur actuel.
|
|
1161
|
-
*
|
|
1162
|
-
* @returns Le lien parent de l'utilisateur ou `null` s'il n'existe pas.
|
|
1163
|
-
* @private
|
|
1164
|
-
*/
|
|
1165
|
-
private _getParentLinkForUser(): ParentLink | null {
|
|
1166
|
-
const { connectTypeDisconnect } = this.parent!._getLinkMeta();
|
|
1167
|
-
const userId = this!.id;
|
|
1168
|
-
if (!userId) return null;
|
|
1169
|
-
return this.parent?.serverData?.links?.[connectTypeDisconnect]?.[userId] || null;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
/**
|
|
1173
|
-
* Valide les préconditions et retourne le lien parent.
|
|
1174
|
-
*
|
|
1175
|
-
* @param methodName - Nom de la méthode appelante (pour les messages d'erreur).
|
|
1176
|
-
* @param expectedTypes - Types d'entité parent autorisés.
|
|
1177
|
-
* @param options - Options de validation.
|
|
1178
|
-
* @param options.silent - Si `true`, retourne `null` au lieu de lever une exception. Par défaut `true`.
|
|
1179
|
-
* @returns - Le lien parent de l'utilisateur, ou `null` si validation échoue.
|
|
1180
|
-
* @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies.
|
|
1181
|
-
* @private
|
|
1182
|
-
*/
|
|
1183
|
-
private _getValidatedParentLink(
|
|
1184
|
-
methodName: string,
|
|
1185
|
-
expectedTypes: string[],
|
|
1186
|
-
options?: { silent?: boolean }
|
|
1187
|
-
): ParentLink | null {
|
|
1188
|
-
const silent = options?.silent ?? true;
|
|
1189
|
-
try {
|
|
1190
|
-
this._validateBasePreconditions(methodName, expectedTypes);
|
|
1191
|
-
} catch (e) {
|
|
1192
|
-
if (silent) return null;
|
|
1193
|
-
throw e;
|
|
1194
|
-
}
|
|
1195
|
-
return this._getParentLinkForUser();
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* Rafraîchit l'utilisateur et l'entité parente après une action.
|
|
1200
|
-
* @private
|
|
1201
|
-
*/
|
|
1202
|
-
private async _refreshWithParent(): Promise<void> {
|
|
1203
|
-
await this.refresh();
|
|
1204
|
-
if (this.parent) {
|
|
1205
|
-
await this.parent.refresh();
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
/**
|
|
1210
|
-
* Vérifie si l'utilisateur est administrateur de l'entité parente.
|
|
1211
|
-
*
|
|
1212
|
-
* Cette méthode permet de vérifier si un utilisateur possède les droits d'administration
|
|
1213
|
-
* sur l'organisation ou le projet parent. Elle est particulièrement utile après avoir
|
|
1214
|
-
* récupéré des membres via `getMembers()` pour déterminer leur niveau de permissions.
|
|
1215
|
-
*
|
|
1216
|
-
* @returns `true` si l'utilisateur est administrateur de l'entité parente, `false` sinon.
|
|
1217
|
-
*
|
|
1218
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1219
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
|
|
1220
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré (pas d'ID).
|
|
1221
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée (pas d'ID).
|
|
1222
|
-
* @throws {ApiError} 400 - Si l'entité parente n'est pas de type "organizations" ou "projects".
|
|
1223
|
-
*
|
|
1224
|
-
* @example
|
|
1225
|
-
* // Vérifier les admins d'une organisation
|
|
1226
|
-
* const org = await me.organization({ slug: "openAtlas" });
|
|
1227
|
-
* const members = await org.getMembers();
|
|
1228
|
-
*
|
|
1229
|
-
* for (const member of members.results) {
|
|
1230
|
-
* if (member.getEntityType() === "citoyens") {
|
|
1231
|
-
* if (member.isAdmin()) {
|
|
1232
|
-
* console.log(`${member.data.name} est administrateur`);
|
|
1233
|
-
* }
|
|
1234
|
-
* }
|
|
1235
|
-
* }
|
|
1236
|
-
*
|
|
1237
|
-
* @example
|
|
1238
|
-
* // Récupérer uniquement les membres admins
|
|
1239
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1240
|
-
* const adminMembers = await org.getMembers({}, { isAdmin: true });
|
|
1241
|
-
*
|
|
1242
|
-
* // Vérifier chaque admin
|
|
1243
|
-
* for (const admin of adminMembers.results) {
|
|
1244
|
-
* if (admin.getEntityType() === "citoyens" && admin.isAdmin()) {
|
|
1245
|
-
* console.log(`${admin.data.name} a les droits admin`);
|
|
1246
|
-
* }
|
|
1247
|
-
* }
|
|
1248
|
-
*/
|
|
1249
|
-
override isAdmin(options?: { silent?: boolean }): boolean {
|
|
1250
|
-
const parentLink = this._getValidatedParentLink(
|
|
1251
|
-
"isAdmin",
|
|
1252
|
-
["organizations", "projects"],
|
|
1253
|
-
{ silent: options?.silent }
|
|
1254
|
-
);
|
|
1255
|
-
return this._validateUserLink(parentLink) && parentLink?.isAdmin === true;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
/**
|
|
1259
|
-
* Vérifie si l'utilisateur est membre de l'organisation parente.
|
|
1260
|
-
*
|
|
1261
|
-
* Cette méthode permet de vérifier si un utilisateur est membre actif d'une organisation.
|
|
1262
|
-
* Elle est utile pour déterminer si un utilisateur a accès aux ressources de l'organisation.
|
|
1263
|
-
*
|
|
1264
|
-
* @returns `true` si l'utilisateur est membre validé de l'organisation parente, `false` sinon.
|
|
1265
|
-
*
|
|
1266
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1267
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
|
|
1268
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré (pas d'ID).
|
|
1269
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée (pas d'ID).
|
|
1270
|
-
* @throws {ApiError} 400 - Si l'entité parente n'est pas de type "organizations".
|
|
1271
|
-
*
|
|
1272
|
-
* @example
|
|
1273
|
-
* // Vérifier les membres d'une organisation
|
|
1274
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1275
|
-
* const members = await org.getMembers();
|
|
1276
|
-
*
|
|
1277
|
-
* for (const member of members.results) {
|
|
1278
|
-
* if (member.getEntityType() === "citoyens" && member.isMember()) {
|
|
1279
|
-
* console.log(`${member.data.name} est membre de l'organisation`);
|
|
1280
|
-
* }
|
|
1281
|
-
* }
|
|
1282
|
-
*/
|
|
1283
|
-
override isMember(options?: { silent?: boolean }): boolean {
|
|
1284
|
-
const parentLink = this._getValidatedParentLink(
|
|
1285
|
-
"isMember",
|
|
1286
|
-
["organizations"],
|
|
1287
|
-
{ silent: options?.silent }
|
|
1288
|
-
);
|
|
1289
|
-
return this._validateUserLink(parentLink);
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
/**
|
|
1293
|
-
* Vérifie si l'utilisateur est contributeur du projet parent.
|
|
1294
|
-
*
|
|
1295
|
-
* Cette méthode permet de vérifier si un utilisateur est contributeur actif d'un projet.
|
|
1296
|
-
* Elle est utile pour déterminer si un utilisateur peut participer aux activités du projet.
|
|
1297
|
-
*
|
|
1298
|
-
* @returns `true` si l'utilisateur est contributeur validé du projet parent, `false` sinon.
|
|
1299
|
-
*
|
|
1300
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1301
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
|
|
1302
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré (pas d'ID).
|
|
1303
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée (pas d'ID).
|
|
1304
|
-
* @throws {ApiError} 400 - Si l'entité parente n'est pas de type "projects".
|
|
1305
|
-
*
|
|
1306
|
-
* @example
|
|
1307
|
-
* // Vérifier les contributeurs d'un projet
|
|
1308
|
-
* const project = await me.project({ slug: "myProject" });
|
|
1309
|
-
* const contributors = await project.getContributors();
|
|
1310
|
-
*
|
|
1311
|
-
* for (const contributor of contributors.results) {
|
|
1312
|
-
* if (contributor.getEntityType() === "citoyens" && contributor.isContributor()) {
|
|
1313
|
-
* console.log(`${contributor.data.name} est contributeur du projet`);
|
|
1314
|
-
* }
|
|
1315
|
-
* }
|
|
1316
|
-
*/
|
|
1317
|
-
override isContributor(options?: { silent?: boolean }): boolean {
|
|
1318
|
-
const parentLink = this._getValidatedParentLink(
|
|
1319
|
-
"isContributor",
|
|
1320
|
-
["projects"],
|
|
1321
|
-
{ silent: options?.silent }
|
|
1322
|
-
);
|
|
1323
|
-
return this._validateUserLink(parentLink);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
/**
|
|
1327
|
-
* Vérifie si l'utilisateur est participant de l'événement parent.
|
|
1328
|
-
*
|
|
1329
|
-
* Cette méthode permet de vérifier si un utilisateur est inscrit comme participant à un événement.
|
|
1330
|
-
* Elle est utile pour déterminer si un utilisateur a confirmé sa participation à l'événement.
|
|
1331
|
-
*
|
|
1332
|
-
* @returns `true` si l'utilisateur est participant validé de l'événement parent, `false` sinon.
|
|
1333
|
-
*
|
|
1334
|
-
* @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté.
|
|
1335
|
-
* @throws {ApiError} 401 - Si l'utilisateur connecté n'est pas administrateur de l'entité parente.
|
|
1336
|
-
* @throws {ApiError} 404 - Si l'utilisateur n'est pas enregistré (pas d'ID).
|
|
1337
|
-
* @throws {ApiError} 404 - Si l'entité parente n'est pas enregistrée (pas d'ID).
|
|
1338
|
-
* @throws {ApiError} 400 - Si l'entité parente n'est pas de type "events".
|
|
1339
|
-
*
|
|
1340
|
-
* @example
|
|
1341
|
-
* // Vérifier les participants d'un événement
|
|
1342
|
-
* const event = await me.event({ slug: "myEvent" });
|
|
1343
|
-
* const attendees = await event.getAttendees();
|
|
1344
|
-
*
|
|
1345
|
-
* for (const attendee of attendees.results) {
|
|
1346
|
-
* if (attendee.getEntityType() === "citoyens" && attendee.isAttendee()) {
|
|
1347
|
-
* console.log(`${attendee.data.name} participe à l'événement`);
|
|
1348
|
-
* }
|
|
1349
|
-
* }
|
|
1350
|
-
*/
|
|
1351
|
-
override isAttendee(options?: { silent?: boolean }): boolean {
|
|
1352
|
-
const parentLink = this._getValidatedParentLink(
|
|
1353
|
-
"isAttendee",
|
|
1354
|
-
["events"],
|
|
1355
|
-
{ silent: options?.silent }
|
|
1356
|
-
);
|
|
1357
|
-
return this._validateUserLink(parentLink);
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
/**
|
|
1361
|
-
* Vérifie si l'utilisateur a une invitation en attente d'acceptation pour l'entité parente.
|
|
1362
|
-
*
|
|
1363
|
-
* Cette méthode vérifie si l'utilisateur a été invité à rejoindre l'organisation, le projet
|
|
1364
|
-
* ou l'événement parent, mais n'a pas encore accepté l'invitation (`isInviting: true`).
|
|
1365
|
-
*
|
|
1366
|
-
* @returns {boolean} `true` si l'utilisateur a une invitation en attente, `false` sinon
|
|
1367
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
|
|
1368
|
-
*
|
|
1369
|
-
* @example
|
|
1370
|
-
* ```typescript
|
|
1371
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1372
|
-
* const members = await org.getMembers();
|
|
1373
|
-
* const invitedUser = members.results.find(m => m.isInviting());
|
|
1374
|
-
* console.log("Invitation en attente:", invitedUser?.data.name);
|
|
1375
|
-
* ```
|
|
1376
|
-
*/
|
|
1377
|
-
override isInviting(options?: { silent?: boolean }): boolean {
|
|
1378
|
-
const parentLink = this._getValidatedParentLink(
|
|
1379
|
-
"isInviting",
|
|
1380
|
-
["citoyens", "organizations", "projects", "events"],
|
|
1381
|
-
{ silent: options?.silent }
|
|
1382
|
-
);
|
|
1383
|
-
if (!parentLink) return false;
|
|
1384
|
-
return parentLink.isInviting === true;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
/**
|
|
1388
|
-
* Vérifie si l'utilisateur connecté a envoyé une invitation d'ami à cet utilisateur.
|
|
1389
|
-
*
|
|
1390
|
-
* @param options - Options de vérification.
|
|
1391
|
-
* @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`.
|
|
1392
|
-
* @returns - `true` si une invitation d'ami est en attente, `false` sinon.
|
|
1393
|
-
*/
|
|
1394
|
-
isInvitingFriend(options?: { silent?: boolean }): boolean {
|
|
1395
|
-
const userLink = this._getValidatedUserLink(
|
|
1396
|
-
"isInvitingFriend",
|
|
1397
|
-
["citoyens", "organizations", "projects", "events"],
|
|
1398
|
-
{ silent: options?.silent }
|
|
1399
|
-
);
|
|
1400
|
-
if (!userLink) return false;
|
|
1401
|
-
return userLink.isInviting === true;
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
/**
|
|
1405
|
-
* Vérifie si l'utilisateur a une invitation en attente avec des droits d'admin.
|
|
1406
|
-
*
|
|
1407
|
-
* Cette méthode vérifie si l'utilisateur a été invité à rejoindre l'organisation ou le projet
|
|
1408
|
-
* parent en tant qu'administrateur, mais n'a pas encore accepté (`isInviting: true` et `isAdmin: true`).
|
|
1409
|
-
*
|
|
1410
|
-
* @returns {boolean} `true` si l'utilisateur a une invitation admin en attente, `false` sinon
|
|
1411
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
|
|
1412
|
-
*
|
|
1413
|
-
* @example
|
|
1414
|
-
* ```typescript
|
|
1415
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1416
|
-
* const members = await org.getMembers();
|
|
1417
|
-
* const invitedAdmin = members.results.find(m => m.isInvitingAdmin());
|
|
1418
|
-
* console.log("Invitation admin en attente:", invitedAdmin?.data.name);
|
|
1419
|
-
* ```
|
|
1420
|
-
*/
|
|
1421
|
-
override isInvitingAdmin(options?: { silent?: boolean }): boolean {
|
|
1422
|
-
const parentLink = this._getValidatedParentLink(
|
|
1423
|
-
"isInvitingAdmin",
|
|
1424
|
-
["organizations", "projects"],
|
|
1425
|
-
{ silent: options?.silent }
|
|
1426
|
-
);
|
|
1427
|
-
if (!parentLink) return false;
|
|
1428
|
-
return (parentLink.isAdminInviting === true || parentLink.isInviting === true) && parentLink.isAdmin === true;
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
/**
|
|
1432
|
-
* Vérifie si l'utilisateur a une demande de promotion admin en attente de validation.
|
|
1433
|
-
*
|
|
1434
|
-
* Cette méthode vérifie si l'utilisateur a demandé ou a été proposé pour devenir administrateur
|
|
1435
|
-
* de l'organisation ou du projet parent, mais la demande n'a pas encore été validée (`isAdminPending: true`).
|
|
1436
|
-
*
|
|
1437
|
-
* @returns {boolean} `true` si l'utilisateur a une demande admin en attente, `false` sinon
|
|
1438
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
|
|
1439
|
-
*
|
|
1440
|
-
* @example
|
|
1441
|
-
* ```typescript
|
|
1442
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1443
|
-
* const members = await org.getMembers();
|
|
1444
|
-
* const pendingAdmin = members.results.find(m => m.isAdminPending());
|
|
1445
|
-
* if (pendingAdmin) {
|
|
1446
|
-
* await pendingAdmin.validateAdminRequest();
|
|
1447
|
-
* }
|
|
1448
|
-
* ```
|
|
1449
|
-
*/
|
|
1450
|
-
override isAdminPending(options?: { silent?: boolean }): boolean {
|
|
1451
|
-
const parentLink = this._getValidatedParentLink(
|
|
1452
|
-
"isAdminPending",
|
|
1453
|
-
["organizations", "projects"],
|
|
1454
|
-
{ silent: options?.silent }
|
|
1455
|
-
);
|
|
1456
|
-
if (!parentLink) return false;
|
|
1457
|
-
return parentLink.isAdmin === true && parentLink.isAdminPending === true;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
/**
|
|
1461
|
-
* Vérifie si l'utilisateur a une demande d'adhésion en attente de validation.
|
|
1462
|
-
*
|
|
1463
|
-
* Cette méthode vérifie si l'utilisateur a demandé à rejoindre l'organisation, le projet
|
|
1464
|
-
* ou l'événement parent, mais la demande n'a pas encore été validée par un administrateur (`toBeValidated: true`).
|
|
1465
|
-
*
|
|
1466
|
-
* @returns {boolean} `true` si l'utilisateur a une demande en attente, `false` sinon
|
|
1467
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin du parent, ou si le parent n'est pas défini
|
|
1468
|
-
*
|
|
1469
|
-
* @example
|
|
1470
|
-
* ```typescript
|
|
1471
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1472
|
-
* const members = await org.getMembers();
|
|
1473
|
-
* const pendingUser = members.results.find(m => m.isToBeValidated());
|
|
1474
|
-
* if (pendingUser) {
|
|
1475
|
-
* await pendingUser.validateMemberRequest();
|
|
1476
|
-
* }
|
|
1477
|
-
* ```
|
|
1478
|
-
*/
|
|
1479
|
-
override isToBeValidated(options?: { silent?: boolean }): boolean {
|
|
1480
|
-
const parentLink = this._getValidatedParentLink(
|
|
1481
|
-
"isToBeValidated",
|
|
1482
|
-
["citoyens", "organizations", "projects", "events"],
|
|
1483
|
-
{ silent: options?.silent }
|
|
1484
|
-
);
|
|
1485
|
-
if (!parentLink) return false;
|
|
1486
|
-
return parentLink.toBeValidated === true;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
/**
|
|
1490
|
-
* Vérifie si cet utilisateur a envoyé une demande d'ami en attente de validation.
|
|
1491
|
-
*
|
|
1492
|
-
* @param options - Options de vérification.
|
|
1493
|
-
* @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`.
|
|
1494
|
-
* @returns - `true` si une demande d'ami est en attente de validation, `false` sinon.
|
|
1495
|
-
*/
|
|
1496
|
-
isToBeValidatedFriend(options?: { silent?: boolean }): boolean {
|
|
1497
|
-
const userLink = this._getValidatedUserLink(
|
|
1498
|
-
"isToBeValidatedFriend",
|
|
1499
|
-
["citoyens", "organizations", "projects", "events"],
|
|
1500
|
-
{ silent: options?.silent }
|
|
1501
|
-
);
|
|
1502
|
-
if (!userLink) return false;
|
|
1503
|
-
return userLink.toBeValidated === true;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
/**
|
|
1507
|
-
* Envoie une demande pour rejoindre l'entité parente.
|
|
1508
|
-
*
|
|
1509
|
-
* Cette méthode permet à un admin de créer une demande de connexion pour un utilisateur
|
|
1510
|
-
* vers l'organisation ou le projet parent. Si l'utilisateur n'a pas encore de lien,
|
|
1511
|
-
* une demande de connexion est créée.
|
|
1512
|
-
*
|
|
1513
|
-
* @param {Object} [options] - Options de la méthode
|
|
1514
|
-
* @param {boolean} [options.admin=false] - Si true, utilise "admin" comme connectType au lieu du type par défaut (connectTypeConnect)
|
|
1515
|
-
* @returns {Promise<unknown>} La réponse de l'API après création de la demande
|
|
1516
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1517
|
-
* @throws {ApiError} Si l'utilisateur est déjà en attente de validation
|
|
1518
|
-
* @throws {ApiError} Si l'utilisateur est déjà connecté à l'entité
|
|
1519
|
-
*
|
|
1520
|
-
* @example
|
|
1521
|
-
* ```typescript
|
|
1522
|
-
* // Un admin récupère les membres et envoie une invitation (type par défaut)
|
|
1523
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1524
|
-
* const users = await org.getMembers();
|
|
1525
|
-
* const user = users.results[0];
|
|
1526
|
-
* await user.sendRequestToJoinParent();
|
|
1527
|
-
*
|
|
1528
|
-
* // Avec le type admin explicite
|
|
1529
|
-
* await user.sendRequestToJoinParent({ admin: true });
|
|
1530
|
-
* ```
|
|
1531
|
-
*/
|
|
1532
|
-
async sendRequestToJoinParent({ admin }: { admin: boolean } = { admin: false }): Promise<unknown> {
|
|
1533
|
-
// Validation selon le type d'invitation : admin requis pour inviter en tant qu'admin
|
|
1534
|
-
if (admin) {
|
|
1535
|
-
// Invitation admin : uniquement orga/project, seul un admin peut le faire
|
|
1536
|
-
this._validateAdminPreconditions("sendRequestToJoinParent", ["organizations", "projects"]);
|
|
1537
|
-
} else {
|
|
1538
|
-
// Invitation simple : orga/project/event, membre/contributeur/connecté peut le faire
|
|
1539
|
-
this._validateMemberPreconditions("sendRequestToJoinParent", ["organizations", "projects", "events"]);
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
const { connectTypeConnect } = this.parent!._getLinkMeta();
|
|
1543
|
-
|
|
1544
|
-
const parentLink = this._getParentLinkForUser();
|
|
1545
|
-
|
|
1546
|
-
// Cas : aucun lien → on demande à se connecter
|
|
1547
|
-
if (!parentLink) {
|
|
1548
|
-
|
|
1549
|
-
const t = this.parent!.getEntityType();
|
|
1550
|
-
|
|
1551
|
-
// Narrow de type pour TypeScript
|
|
1552
|
-
const parentType = t as ConnectData["parentType"];
|
|
1553
|
-
|
|
1554
|
-
const data: ConnectData = {
|
|
1555
|
-
childId: this.id!,
|
|
1556
|
-
childType: "citoyens",
|
|
1557
|
-
parentType,
|
|
1558
|
-
parentId: this.parent!.id!,
|
|
1559
|
-
connectType: admin ? "admin" : connectTypeConnect
|
|
1560
|
-
};
|
|
1561
|
-
|
|
1562
|
-
const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
|
|
1563
|
-
await this._refreshWithParent();
|
|
1564
|
-
return retour;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
// Cas : déjà en attente d'acceptation
|
|
1568
|
-
if (parentLink.isInviting || parentLink?.isAdminInviting) {
|
|
1569
|
-
throw new ApiError("Une invitation est déjà en attente d'acceptation.", 400);
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
// Cas par défaut : rien à faire
|
|
1573
|
-
throw new ApiError("Vous êtes déjà connecté à cette entité.", 400);
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
/**
|
|
1577
|
-
* Valide une demande de membre en attente de validation.
|
|
1578
|
-
*
|
|
1579
|
-
* Cette méthode permet à un admin de valider une demande d'adhésion d'un utilisateur
|
|
1580
|
-
* qui est en attente (`toBeValidated: true`). Après validation, l'utilisateur devient
|
|
1581
|
-
* membre actif de l'entité parente.
|
|
1582
|
-
*
|
|
1583
|
-
* @returns {Promise<unknown>} La réponse de l'API après validation
|
|
1584
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1585
|
-
* @throws {ApiError} Si l'utilisateur n'a pas de demande en attente
|
|
1586
|
-
*
|
|
1587
|
-
* @example
|
|
1588
|
-
* ```typescript
|
|
1589
|
-
* // Un admin valide une demande en attente
|
|
1590
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1591
|
-
* const members = await org.getMembers();
|
|
1592
|
-
* const pendingUser = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.toBeValidated);
|
|
1593
|
-
* await pendingUser.validateMemberRequest();
|
|
1594
|
-
* ```
|
|
1595
|
-
*/
|
|
1596
|
-
async validateMemberRequest(): Promise<unknown> {
|
|
1597
|
-
this._validateAdminPreconditions("validateMemberRequest", ["organizations", "projects"]);
|
|
1598
|
-
|
|
1599
|
-
const parentLink = this._getParentLinkForUser();
|
|
1600
|
-
|
|
1601
|
-
if (!parentLink?.toBeValidated) {
|
|
1602
|
-
throw new ApiError("Cet utilisateur n'a pas de demande en attente de validation.", 400);
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
const t = this.parent!.getEntityType();
|
|
1606
|
-
|
|
1607
|
-
const parentType = t as LinkValidateData["parentType"];
|
|
1608
|
-
|
|
1609
|
-
const data: LinkValidateData = {
|
|
1610
|
-
childId: this.id!,
|
|
1611
|
-
childType: "citoyens",
|
|
1612
|
-
parentType,
|
|
1613
|
-
parentId: this.parent!.id!,
|
|
1614
|
-
linkOption: "toBeValidated"
|
|
1615
|
-
};
|
|
1616
|
-
|
|
1617
|
-
const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
|
|
1618
|
-
await this._refreshWithParent();
|
|
1619
|
-
return retour;
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
/**
|
|
1623
|
-
* Valide une demande d'admin en attente.
|
|
1624
|
-
*
|
|
1625
|
-
* Cette méthode permet à un admin de valider une demande de promotion au statut d'admin
|
|
1626
|
-
* pour un utilisateur qui est en attente (`isAdminPending: true`). Après validation,
|
|
1627
|
-
* l'utilisateur devient admin de l'entité parente.
|
|
1628
|
-
*
|
|
1629
|
-
* @returns {Promise<unknown>} La réponse de l'API après validation
|
|
1630
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1631
|
-
* @throws {ApiError} Si l'utilisateur n'a pas de demande d'admin en attente
|
|
1632
|
-
*
|
|
1633
|
-
* @example
|
|
1634
|
-
* ```typescript
|
|
1635
|
-
* // Un admin valide une demande de promotion à admin
|
|
1636
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1637
|
-
* const members = await org.getMembers();
|
|
1638
|
-
* const pendingAdmin = members.results.find(u => u.serverData.links?.memberOf?.[org.id]?.isAdminPending);
|
|
1639
|
-
* await pendingAdmin.validateAdminRequest();
|
|
1640
|
-
* ```
|
|
1641
|
-
*/
|
|
1642
|
-
async validateAdminRequest(): Promise<unknown> {
|
|
1643
|
-
this._validateAdminPreconditions("validateAdminRequest", ["organizations", "projects"]);
|
|
1644
|
-
|
|
1645
|
-
const parentLink = this._getParentLinkForUser();
|
|
1646
|
-
|
|
1647
|
-
if (!parentLink?.isAdminPending) {
|
|
1648
|
-
throw new ApiError("Cet utilisateur n'a pas de demande d'admin en attente.", 400);
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
const t = this.parent!.getEntityType();
|
|
1652
|
-
|
|
1653
|
-
const parentType = t as LinkValidateData["parentType"];
|
|
1654
|
-
|
|
1655
|
-
const data: LinkValidateData = {
|
|
1656
|
-
childId: this.id!,
|
|
1657
|
-
childType: "citoyens",
|
|
1658
|
-
parentType,
|
|
1659
|
-
parentId: this.parent!.id!,
|
|
1660
|
-
linkOption: "isAdminPending"
|
|
1661
|
-
};
|
|
1662
|
-
|
|
1663
|
-
const retour = await this.callIsConnected(() => this.endpointApi.linkValidate(data));
|
|
1664
|
-
await this._refreshWithParent();
|
|
1665
|
-
return retour;
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
/**
|
|
1669
|
-
* Retire un utilisateur de l'entité parente.
|
|
1670
|
-
*
|
|
1671
|
-
* Cette méthode permet à un admin de déconnecter un membre de l'organisation ou du projet parent.
|
|
1672
|
-
* L'utilisateur perd tous ses liens avec l'entité (membre, admin, etc.).
|
|
1673
|
-
*
|
|
1674
|
-
* @returns {Promise<unknown>} La réponse de l'API après déconnexion
|
|
1675
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1676
|
-
* @throws {ApiError} Si l'utilisateur n'est pas membre de l'entité
|
|
1677
|
-
*
|
|
1678
|
-
* @example
|
|
1679
|
-
* ```typescript
|
|
1680
|
-
* // Un admin retire un membre
|
|
1681
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1682
|
-
* const members = await org.getMembers();
|
|
1683
|
-
* const userToRemove = members.results[0];
|
|
1684
|
-
* await userToRemove.removeFromParent();
|
|
1685
|
-
* ```
|
|
1686
|
-
*/
|
|
1687
|
-
async removeFromParent(): Promise<unknown> {
|
|
1688
|
-
this._validateAdminPreconditions("removeFromParent", ["organizations", "projects"]);
|
|
1689
|
-
|
|
1690
|
-
const parentLink = this._getParentLinkForUser();
|
|
1691
|
-
|
|
1692
|
-
if (!parentLink) {
|
|
1693
|
-
throw new ApiError("Cet utilisateur n'est pas connecté à cette entité.", 400);
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
const t = this.parent!.getEntityType();
|
|
1697
|
-
|
|
1698
|
-
const parentType = t as DisconnectData["parentType"];
|
|
1699
|
-
|
|
1700
|
-
const { connectTypeDisconnect } = this.parent!._getLinkMeta();
|
|
1701
|
-
|
|
1702
|
-
const data: DisconnectData = {
|
|
1703
|
-
childId: this.id!,
|
|
1704
|
-
childType: "citoyens",
|
|
1705
|
-
parentType,
|
|
1706
|
-
parentId: this.parent!.id!,
|
|
1707
|
-
connectType: connectTypeDisconnect
|
|
1708
|
-
};
|
|
1709
|
-
|
|
1710
|
-
const retour = await this.callIsConnected(() => this.endpointApi.disconnect(data));
|
|
1711
|
-
await this._refreshWithParent();
|
|
1712
|
-
return retour;
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
/**
|
|
1716
|
-
* Promeut un utilisateur au statut d'admin de l'entité parente.
|
|
1717
|
-
*
|
|
1718
|
-
* Cette méthode permet à un admin de promouvoir un membre ordinaire au statut d'admin
|
|
1719
|
-
* de l'organisation ou du projet parent.
|
|
1720
|
-
*
|
|
1721
|
-
* @returns {Promise<unknown>} La réponse de l'API après promotion
|
|
1722
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1723
|
-
* @throws {ApiError} Si l'utilisateur n'est pas membre de l'entité
|
|
1724
|
-
* @throws {ApiError} Si l'utilisateur est déjà admin
|
|
1725
|
-
*
|
|
1726
|
-
* @example
|
|
1727
|
-
* ```typescript
|
|
1728
|
-
* // Un admin promeut un membre au statut d'admin
|
|
1729
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1730
|
-
* const members = await org.getMembers();
|
|
1731
|
-
* const userToPromote = members.results.find(u => !u.isAdmin());
|
|
1732
|
-
* await userToPromote.promoteToAdmin();
|
|
1733
|
-
* ```
|
|
1734
|
-
*/
|
|
1735
|
-
async promoteToAdmin(): Promise<unknown> {
|
|
1736
|
-
this._validateAdminPreconditions("promoteToAdmin", ["organizations", "projects"]);
|
|
1737
|
-
|
|
1738
|
-
const parentLink = this._getParentLinkForUser();
|
|
1739
|
-
|
|
1740
|
-
if (!parentLink) {
|
|
1741
|
-
throw new ApiError("Cet utilisateur n'est pas membre de cette entité.", 400);
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
if (parentLink?.isAdmin && (parentLink?.isInviting || parentLink?.isAdminInviting)) {
|
|
1745
|
-
throw new ApiError("Cet utilisateur est déjà en cours d'invitation en tant qu'admin.", 400);
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
if (parentLink?.isAdmin && !parentLink?.isInviting && !parentLink?.isAdminInviting) {
|
|
1749
|
-
throw new ApiError("Cet utilisateur est déjà admin.", 400);
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
const t = this.parent!.getEntityType();
|
|
1753
|
-
|
|
1754
|
-
const parentType = t as ConnectData["parentType"];
|
|
1755
|
-
|
|
1756
|
-
const data: ConnectData = {
|
|
1757
|
-
childId: this.id!,
|
|
1758
|
-
childType: "citoyens",
|
|
1759
|
-
parentType,
|
|
1760
|
-
parentId: this.parent!.id!,
|
|
1761
|
-
connectType: "admin"
|
|
1762
|
-
};
|
|
1763
|
-
|
|
1764
|
-
const retour = await this.callIsConnected(() => this.endpointApi.connect(data));
|
|
1765
|
-
await this._refreshWithParent();
|
|
1766
|
-
return retour;
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
/**
|
|
1770
|
-
* Rétrograde un admin au statut de membre ordinaire.
|
|
1771
|
-
*
|
|
1772
|
-
* Cette méthode permet à un admin de rétrograder un autre admin au statut de membre
|
|
1773
|
-
* ordinaire de l'organisation ou du projet parent. L'utilisateur perd ses privilèges d'admin
|
|
1774
|
-
* mais reste membre de l'entité.
|
|
1775
|
-
*
|
|
1776
|
-
* @returns {Promise<unknown>} La réponse de l'API après rétrogradation
|
|
1777
|
-
* @throws {ApiError} Si l'utilisateur n'est pas connecté, pas admin, ou si le parent n'est pas défini
|
|
1778
|
-
* @throws {ApiError} Si l'utilisateur n'est pas admin de l'entité
|
|
1779
|
-
*
|
|
1780
|
-
* @example
|
|
1781
|
-
* ```typescript
|
|
1782
|
-
* // Un admin rétrograde un autre admin
|
|
1783
|
-
* const org = await me.organization({ slug: "myOrg" });
|
|
1784
|
-
* const admins = await org.getMembers({}, { isAdmin: true });
|
|
1785
|
-
* const adminToDemote = admins.results.find(u => u.isAdmin());
|
|
1786
|
-
* await adminToDemote.demoteFromAdmin();
|
|
1787
|
-
* ```
|
|
1788
|
-
*/
|
|
1789
|
-
async demoteFromAdmin(): Promise<unknown> {
|
|
1790
|
-
this._validateAdminPreconditions("demoteFromAdmin", ["organizations", "projects"]);
|
|
1791
|
-
|
|
1792
|
-
const parentLink = this._getParentLinkForUser();
|
|
1793
|
-
|
|
1794
|
-
if (!parentLink?.isAdmin || parentLink.isAdminPending) {
|
|
1795
|
-
throw new ApiError("Cet utilisateur n'est pas admin de cette entité.", 400);
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
const t = this.parent!.getEntityType();
|
|
1799
|
-
|
|
1800
|
-
const { connectTypeDisconnect } = this.parent!._getLinkMeta();
|
|
1801
|
-
|
|
1802
|
-
const parentType = t as DemoteAdminData["parentType"];
|
|
1803
|
-
|
|
1804
|
-
// Pour rétrograder un admin, on déconnecte le lien "admin"
|
|
1805
|
-
// L'utilisateur garde son lien "member"
|
|
1806
|
-
const data: DemoteAdminData = {
|
|
1807
|
-
childId: this.id!,
|
|
1808
|
-
childType: "citoyens",
|
|
1809
|
-
parentType,
|
|
1810
|
-
parentId: this.parent!.id!,
|
|
1811
|
-
connect: connectTypeDisconnect as DemoteAdminData["connect"],
|
|
1812
|
-
isAdmin: false
|
|
1813
|
-
};
|
|
1814
|
-
|
|
1815
|
-
const retour = await this.callIsConnected(() => this.endpointApi.demoteAdmin(data));
|
|
1816
|
-
await this._refreshWithParent();
|
|
1817
|
-
return retour;
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
// ===========================================================================
|
|
1821
|
-
// Notifications (sous-ressource de l'utilisateur connecté)
|
|
1822
|
-
// ===========================================================================
|
|
1823
|
-
|
|
1824
|
-
/** Gestionnaire de notifications, créé paresseusement et mis en cache. */
|
|
1825
|
-
private _notifications?: Notifications;
|
|
1826
|
-
|
|
1827
|
-
/**
|
|
1828
|
-
* Point d'entrée `me.notifications` (composition, PAS une entité BaseEntity).
|
|
1829
|
-
* Sûr : endpointApi/apiClient sont initialisés dans le constructeur avant tout accès getter.
|
|
1830
|
-
*/
|
|
1831
|
-
get notifications(): Notifications {
|
|
1832
|
-
return (this._notifications ??= new Notifications(this));
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
/**
|
|
1836
|
-
* Récupère les notifications (mode liste). STATELESS + plat (`NotificationItemData[]`),
|
|
1837
|
-
* idéal comme `queryFn` React Query / prefetch SSR — ne touche PAS le manager réactif
|
|
1838
|
-
* (`me.notifications`). `ApiClient._transformData` garantit `notif` = tableau (map -> array
|
|
1839
|
-
* + `id` injecté), donc on fait confiance à la normalisation amont.
|
|
1840
|
-
* pathParams.id est OBLIGATOIRE : le défaut "@userId" ne matche pas le pattern de l'endpoint.
|
|
1841
|
-
*/
|
|
1842
|
-
async fetchNotifications({ indexMin }: { indexMin?: number } = {}): Promise<NotificationItemData[]> {
|
|
1843
|
-
const res = (await this.callIsMe(() => {
|
|
1844
|
-
const data: GetNotificationsData = {
|
|
1845
|
-
pathParams: { type: "citoyens", id: this.userId! },
|
|
1846
|
-
...(indexMin != null ? { indexMin } : {})
|
|
1847
|
-
};
|
|
1848
|
-
return this.endpointApi.getNotifications(data);
|
|
1849
|
-
})) as { notif?: NotificationItemData[] };
|
|
1850
|
-
return Array.isArray(res?.notif) ? res.notif : [];
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
/**
|
|
1854
|
-
* Compte les notifications NON VUES (badge). STATELESS. refreshTimestamp = maintenant ->
|
|
1855
|
-
* le serveur renvoie `countNotif` (total non-vus) + une liste vide.
|
|
1856
|
-
*/
|
|
1857
|
-
async fetchNotificationsCount(): Promise<number> {
|
|
1858
|
-
const res = (await this.callIsMe(() => {
|
|
1859
|
-
const data: GetNotificationsCountData = {
|
|
1860
|
-
pathParams: { type: "citoyens", id: this.userId! },
|
|
1861
|
-
refreshTimestamp: Math.floor(Date.now() / 1000)
|
|
1862
|
-
};
|
|
1863
|
-
return this.endpointApi.getNotificationsCount(data);
|
|
1864
|
-
})) as { countNotif?: number };
|
|
1865
|
-
return typeof res?.countNotif === "number" ? res.countNotif : 0;
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
/** Marque une notification comme lue (MARK_NOTIFICATION_AS_READ). */
|
|
1869
|
-
async markNotificationAsRead(id: string): Promise<unknown> {
|
|
1870
|
-
const data: MarkNotificationAsReadData = { id };
|
|
1871
|
-
return this.callIsMe(() => this.endpointApi.markNotificationAsRead(data));
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
/** Marque toutes les notifications comme vues/lues (NOTIFICATION_UPDATE). */
|
|
1875
|
-
async markAllNotifications(action: "seen" | "read"): Promise<unknown> {
|
|
1876
|
-
const data: NotificationUpdateData = { action, all: true };
|
|
1877
|
-
return this.callIsMe(() => this.endpointApi.notificationUpdate(data));
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
/** Supprime toutes les notifications de l'utilisateur (REMOVE_ALL_NOTIFICATIONS). */
|
|
1881
|
-
async removeAllNotifications(): Promise<unknown> {
|
|
1882
|
-
return this.callIsMe(() => this.endpointApi.removeAllNotifications());
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
// Incorporation des mixins dans User
|
|
1888
|
-
Object.assign(User.prototype, UserMixin);
|
|
1889
|
-
|