@communecter/cocolight-api-client 1.0.123 → 1.0.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cocolight-api-client.browser.js +2 -2
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
- package/package.json +1 -1
- package/src/Api.ts +20 -4
- package/src/ApiClient.ts +28 -0
- package/src/api/BaseEntity.ts +484 -149
- package/src/api/Classified.ts +171 -0
- package/src/api/EndpointApi.ts +291 -118
- package/src/api/EndpointApi.types.ts +524 -18
- package/src/api/EntityRegistry.ts +11 -3
- package/src/api/Event.ts +6 -1
- package/src/api/Organization.ts +15 -2
- package/src/api/Poi.ts +14 -4
- package/src/api/Project.ts +7 -2
- package/src/api/User.ts +1 -0
- package/src/api/UserApi.ts +3 -2
- package/src/api/serverDataType/Classified.ts +152 -0
- package/src/endpoints.module.ts +181 -51
- package/src/index.ts +3 -0
- package/src/types/entities.ts +3 -2
- package/types/Api.d.ts +2 -0
- package/types/ApiClient.d.ts +15 -0
- package/types/api/BaseEntity.d.ts +184 -12
- package/types/api/Classified.d.ts +30 -0
- package/types/api/EndpointApi.d.ts +108 -1
- package/types/api/EndpointApi.types.d.ts +473 -22
- package/types/api/EntityRegistry.d.ts +1 -1
- package/types/api/Organization.d.ts +1 -1
- package/types/api/Poi.d.ts +1 -0
- package/types/api/User.d.ts +1 -0
- package/types/api/serverDataType/Classified.d.ts +72 -0
- package/types/endpoints.module.d.ts +3502 -478
- package/types/index.d.ts +3 -0
- package/types/types/entities.d.ts +3 -2
package/src/api/BaseEntity.ts
CHANGED
|
@@ -49,7 +49,16 @@ import type {
|
|
|
49
49
|
GetCountriesData,
|
|
50
50
|
SearchZonesData,
|
|
51
51
|
CoformAnswersByFormsData,
|
|
52
|
-
FundingEnvelopeData
|
|
52
|
+
FundingEnvelopeData,
|
|
53
|
+
LinkDiscourseAccountData,
|
|
54
|
+
UnlinkDiscourseAccountData,
|
|
55
|
+
DiscourseProfileData,
|
|
56
|
+
DiscourseCheckEmailData,
|
|
57
|
+
DiscourseDismissLinkData,
|
|
58
|
+
LinkMediawikiAccountData,
|
|
59
|
+
UnlinkMediawikiAccountData,
|
|
60
|
+
GetMediawikiContributionsData,
|
|
61
|
+
CoremuOperationData
|
|
53
62
|
} from "./EndpointApi.types.js";
|
|
54
63
|
import type { GetElementsKeyResponse } from "../types/api-responses.js";
|
|
55
64
|
import type { TransformsMap } from "../types/entities.js";
|
|
@@ -65,6 +74,7 @@ type OrganizationInput = { id: string } | { slug: string } | Record<string, any>
|
|
|
65
74
|
type ProjectInput = { id: string } | { slug: string } | Record<string, any>;
|
|
66
75
|
type PoiInput = { id: string } | { slug: string } | Record<string, any>;
|
|
67
76
|
type EventInput = { id: string } | { slug: string } | Record<string, any>;
|
|
77
|
+
type ClassifiedInput = { id: string } | Record<string, any>;
|
|
68
78
|
type BadgeInput = { id: string } | Record<string, any>;
|
|
69
79
|
type NewsInput = { id: string } | Record<string, any>;
|
|
70
80
|
|
|
@@ -73,7 +83,7 @@ type NewsInput = { id: string } | Record<string, any>;
|
|
|
73
83
|
* possédant au moins .toHexString().
|
|
74
84
|
* (peu importe la d.ts du module, on impose notre contrat minimal)
|
|
75
85
|
*/
|
|
76
|
-
const createObjectId: (arg?: number|string|number[]|Buffer) => { toHexString(): string } = objectId as any;
|
|
86
|
+
const createObjectId: (arg?: number | string | number[] | Buffer) => { toHexString(): string } = objectId as any;
|
|
77
87
|
|
|
78
88
|
// Types TypeScript importés
|
|
79
89
|
type ApiClient = import("../ApiClient.js").default;
|
|
@@ -88,9 +98,10 @@ type Badge = import("./Badge.js").Badge;
|
|
|
88
98
|
type Comment = import("./Comment.js").Comment;
|
|
89
99
|
type Answer = import("./Answer.js").Answer;
|
|
90
100
|
type Form = import("./Form.js").Form;
|
|
101
|
+
type Classified = import("./Classified.js").Classified;
|
|
91
102
|
|
|
92
103
|
// Types d'union
|
|
93
|
-
type AnyEntity = User | Organization | Project | Poi | EventEntity | Badge | News | Comment | Answer | Form;
|
|
104
|
+
type AnyEntity = User | Organization | Project | Poi | EventEntity | Badge | News | Comment | Answer | Form | Classified;
|
|
94
105
|
type ParentLike = BaseEntity<any> & { apiClient: ApiClient, userContext?: User | null };
|
|
95
106
|
|
|
96
107
|
// Types pour les dépendances
|
|
@@ -109,6 +120,7 @@ interface Deps {
|
|
|
109
120
|
Comment?: any;
|
|
110
121
|
Answer?: any;
|
|
111
122
|
Form?: any;
|
|
123
|
+
Classified?: any
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
interface BaseEntityConfig {
|
|
@@ -127,6 +139,7 @@ interface EntityTypeMap {
|
|
|
127
139
|
comments: Comment;
|
|
128
140
|
answers: Answer;
|
|
129
141
|
forms: Form;
|
|
142
|
+
classifieds: Classified;
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
// Types pour les streams et uploads
|
|
@@ -135,6 +148,15 @@ type UploadInput = File | Blob | Buffer | import("stream").Readable;
|
|
|
135
148
|
type ValidatedUpload = File | Buffer | ReadableWithMeta;
|
|
136
149
|
|
|
137
150
|
// Type de retour pour fundingEnvelope
|
|
151
|
+
//
|
|
152
|
+
// Note: `projects` est un tableau d'enveloppes de financement.
|
|
153
|
+
// Chaque enveloppe contient les métadonnées (totalFinancement, depenses, actions, etc.)
|
|
154
|
+
// + un sous-champ `project` qui porte l'entité projet réelle.
|
|
155
|
+
export type FundingEnvelopeProjectItem = {
|
|
156
|
+
project?: AnyEntity | object;
|
|
157
|
+
[k: string]: unknown;
|
|
158
|
+
};
|
|
159
|
+
|
|
138
160
|
export type FundingEnvelopeResult = {
|
|
139
161
|
contextData?: AnyEntity | object;
|
|
140
162
|
context?: AnyEntity | object;
|
|
@@ -142,11 +164,22 @@ export type FundingEnvelopeResult = {
|
|
|
142
164
|
form?: object;
|
|
143
165
|
paymentMethods?: object;
|
|
144
166
|
nopropProject?: AnyEntity[] | object;
|
|
145
|
-
projects?:
|
|
167
|
+
projects?: FundingEnvelopeProjectItem[];
|
|
146
168
|
userOrga?: AnyEntity[] | object;
|
|
147
169
|
[k: string]: unknown;
|
|
148
170
|
};
|
|
149
171
|
|
|
172
|
+
// Champs auto-injectés par `_withCostumContext` dans le finalData reçu par le finalizer.
|
|
173
|
+
// Permet de typer correctement le param finalData côté callers sans cast `as any`.
|
|
174
|
+
export type CostumContextFields = {
|
|
175
|
+
costumSlug: string;
|
|
176
|
+
contextId: string;
|
|
177
|
+
contextType: EntityType;
|
|
178
|
+
sourceKey: string[];
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export type WithCostumContext<T> = T & CostumContextFields;
|
|
182
|
+
|
|
150
183
|
// Type pour le curseur de pagination utilisé dans _createPaginatorEngine
|
|
151
184
|
type PaginationCursor = {
|
|
152
185
|
searchType?: string[];
|
|
@@ -203,8 +236,8 @@ export interface PaginatorPage<T> {
|
|
|
203
236
|
*/
|
|
204
237
|
export type ExtractMethodNames<T> = T extends Map<any, infer V>
|
|
205
238
|
? V extends readonly (readonly [any, infer M])[]
|
|
206
|
-
|
|
207
|
-
|
|
239
|
+
? M
|
|
240
|
+
: never
|
|
208
241
|
: never;
|
|
209
242
|
|
|
210
243
|
// LinkMeta interface
|
|
@@ -217,7 +250,7 @@ interface LinkMeta {
|
|
|
217
250
|
// Type du constructeur de BaseEntity avec ses propriétés statiques
|
|
218
251
|
type BaseEntityCtor = typeof BaseEntity & {
|
|
219
252
|
entityTag: string;
|
|
220
|
-
entityType: "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers";
|
|
253
|
+
entityType: "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds";
|
|
221
254
|
SCHEMA_CONSTANTS: string | string[];
|
|
222
255
|
};
|
|
223
256
|
|
|
@@ -311,7 +344,7 @@ interface FinalizerResult<TOut> {
|
|
|
311
344
|
}
|
|
312
345
|
|
|
313
346
|
// Types pour les entités
|
|
314
|
-
type EntityType = "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers";
|
|
347
|
+
export type EntityType = "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds";
|
|
315
348
|
|
|
316
349
|
// ============================================================================
|
|
317
350
|
|
|
@@ -488,11 +521,11 @@ export class BaseEntity<TServerData = any> {
|
|
|
488
521
|
|
|
489
522
|
/** @returns Indique si cette entité représente l'utilisateur connecté */
|
|
490
523
|
get isMe(): boolean {
|
|
491
|
-
return !!(this.isConnected && this.userId && this.userContext?.id && typeof this.userId=== "string" && typeof this.userContext?.id === "string" && this.userId === this.userContext?.id);
|
|
524
|
+
return !!(this.isConnected && this.userId && this.userContext?.id && typeof this.userId === "string" && typeof this.userContext?.id === "string" && this.userId === this.userContext?.id);
|
|
492
525
|
}
|
|
493
526
|
|
|
494
527
|
/** @returns Type de l'entité (ex: 'citoyens') */
|
|
495
|
-
getEntityType(): "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" {
|
|
528
|
+
getEntityType(): "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" {
|
|
496
529
|
return this._getCtor().entityType;
|
|
497
530
|
}
|
|
498
531
|
|
|
@@ -533,7 +566,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
533
566
|
await this._add(payload);
|
|
534
567
|
this._resetInitialDraftData();
|
|
535
568
|
// on refresh le contexte utilisateur si besoin
|
|
536
|
-
if(this.userContext) {
|
|
569
|
+
if (this.userContext) {
|
|
537
570
|
await this.userContext?.refresh();
|
|
538
571
|
}
|
|
539
572
|
return await this.refresh();
|
|
@@ -658,9 +691,9 @@ export class BaseEntity<TServerData = any> {
|
|
|
658
691
|
const initialValue = this._initialDraftData?.[key];
|
|
659
692
|
|
|
660
693
|
const isModified =
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
694
|
+
current !== undefined &&
|
|
695
|
+
initialValue !== undefined &&
|
|
696
|
+
this._serialize(this._toRawDeep(current)) !== this._serialize(initialValue);
|
|
664
697
|
|
|
665
698
|
if (!isModified) {
|
|
666
699
|
this._draftData[key] = draft[key];
|
|
@@ -862,8 +895,8 @@ export class BaseEntity<TServerData = any> {
|
|
|
862
895
|
|
|
863
896
|
if (
|
|
864
897
|
typeof val === "function" ||
|
|
865
|
-
|
|
866
|
-
|
|
898
|
+
typeof val === "symbol" ||
|
|
899
|
+
typeof val === "undefined"
|
|
867
900
|
) {
|
|
868
901
|
continue;
|
|
869
902
|
}
|
|
@@ -991,7 +1024,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
991
1024
|
* @throws {ApiClientError} - Si une erreur se produit lors de l'appel de l'API.
|
|
992
1025
|
*/
|
|
993
1026
|
async callNoConnected(constant: string, data: object = {}): Promise<unknown> {
|
|
994
|
-
if(this.isConnected) {
|
|
1027
|
+
if (this.isConnected) {
|
|
995
1028
|
throw new ApiAuthenticationError("Vous devez ne devez pas être connecté pour faire cette action.", 403);
|
|
996
1029
|
}
|
|
997
1030
|
return this.call(constant, data);
|
|
@@ -1009,7 +1042,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1009
1042
|
* @throws {ApiClientError} - Si une erreur se produit lors de l'appel de l'API.
|
|
1010
1043
|
*/
|
|
1011
1044
|
async callIsConnected(param: string | (() => Promise<any>), data: object = {}): Promise<unknown> {
|
|
1012
|
-
if(!this.isConnected) {
|
|
1045
|
+
if (!this.isConnected) {
|
|
1013
1046
|
throw new ApiAuthenticationError("Vous devez être connecté pour faire cette action.", 401);
|
|
1014
1047
|
}
|
|
1015
1048
|
// Si le premier paramètre est une fonction, on l'exécute en tant que callback
|
|
@@ -1086,12 +1119,12 @@ export class BaseEntity<TServerData = any> {
|
|
|
1086
1119
|
if (!input) {
|
|
1087
1120
|
throw new ApiValidationError("Le fichier est requis.", 400, ["Le fichier est requis"]);
|
|
1088
1121
|
}
|
|
1089
|
-
|
|
1122
|
+
|
|
1090
1123
|
const isNode = typeof window === "undefined" && typeof process !== "undefined";
|
|
1091
1124
|
|
|
1092
1125
|
let output: ValidatedUpload | null = null;
|
|
1093
1126
|
let mimeType = "";
|
|
1094
|
-
|
|
1127
|
+
|
|
1095
1128
|
// Navigateur : File
|
|
1096
1129
|
if (typeof File !== "undefined" && input instanceof File) {
|
|
1097
1130
|
mimeType = input.type;
|
|
@@ -1100,19 +1133,19 @@ export class BaseEntity<TServerData = any> {
|
|
|
1100
1133
|
}
|
|
1101
1134
|
output = input;
|
|
1102
1135
|
}
|
|
1103
|
-
|
|
1136
|
+
|
|
1104
1137
|
// Navigateur : Blob
|
|
1105
1138
|
else if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
1106
1139
|
mimeType = input.type;
|
|
1107
1140
|
if (!allowedMimeTypes.includes(mimeType)) {
|
|
1108
1141
|
throw new ApiValidationError("Le type du fichier est invalide.", 400, ["Le type du fichier est invalide"]);
|
|
1109
1142
|
}
|
|
1110
|
-
|
|
1143
|
+
|
|
1111
1144
|
const ext = mimeType.split("/")[1] || "bin";
|
|
1112
1145
|
const fileName = `${Date.now()}.${ext}`;
|
|
1113
1146
|
output = new File([input], fileName, { type: mimeType });
|
|
1114
1147
|
}
|
|
1115
|
-
|
|
1148
|
+
|
|
1116
1149
|
// Node.js : Buffer
|
|
1117
1150
|
else if (isNode && Buffer.isBuffer(input)) {
|
|
1118
1151
|
const fileTypeResult = await fromBuffer(input);
|
|
@@ -1120,13 +1153,13 @@ export class BaseEntity<TServerData = any> {
|
|
|
1120
1153
|
if (!fileTypeResult) {
|
|
1121
1154
|
throw new ApiValidationError("Le type du fichier est invalide.", 400, ["Le type du fichier est invalide"]);
|
|
1122
1155
|
}
|
|
1123
|
-
|
|
1156
|
+
|
|
1124
1157
|
mimeType = fileTypeResult.mime;
|
|
1125
|
-
|
|
1158
|
+
|
|
1126
1159
|
if (!allowedMimeTypes.includes(mimeType)) {
|
|
1127
1160
|
throw new ApiValidationError("Le type du fichier est invalide.", 400, ["Le type du fichier est invalide"]);
|
|
1128
1161
|
}
|
|
1129
|
-
|
|
1162
|
+
|
|
1130
1163
|
// Pour un fichier image, on transforme en stream
|
|
1131
1164
|
if (expectedType === "image") {
|
|
1132
1165
|
const ext = fileTypeResult.ext;
|
|
@@ -1136,7 +1169,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1136
1169
|
output = input;
|
|
1137
1170
|
}
|
|
1138
1171
|
}
|
|
1139
|
-
|
|
1172
|
+
|
|
1140
1173
|
// Node.js : ReadableStream
|
|
1141
1174
|
else if (isNode && isNodeReadable(input)) {
|
|
1142
1175
|
const readableIn = input as import("stream").Readable;
|
|
@@ -1175,11 +1208,11 @@ export class BaseEntity<TServerData = any> {
|
|
|
1175
1208
|
|
|
1176
1209
|
output = resultStream as ReadableWithMeta;
|
|
1177
1210
|
}
|
|
1178
|
-
|
|
1211
|
+
|
|
1179
1212
|
else {
|
|
1180
1213
|
throw new ApiValidationError("Type de fichier non reconnu.", 400, ["Type de fichier non reconnu."]);
|
|
1181
1214
|
}
|
|
1182
|
-
|
|
1215
|
+
|
|
1183
1216
|
return output!;
|
|
1184
1217
|
}
|
|
1185
1218
|
|
|
@@ -1214,7 +1247,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1214
1247
|
throw new Error("bufferToReadable ne doit pas être appelé dans le navigateur");
|
|
1215
1248
|
}
|
|
1216
1249
|
}
|
|
1217
|
-
|
|
1250
|
+
|
|
1218
1251
|
/**
|
|
1219
1252
|
* Crée un PassThrough stream pour le traitement des fichiers.
|
|
1220
1253
|
*
|
|
@@ -1330,7 +1363,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1330
1363
|
}
|
|
1331
1364
|
});
|
|
1332
1365
|
}
|
|
1333
|
-
|
|
1366
|
+
|
|
1334
1367
|
/**
|
|
1335
1368
|
* Génère un nouvel identifiant unique.
|
|
1336
1369
|
* @returns Un identifiant unique.
|
|
@@ -1543,7 +1576,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1543
1576
|
*/
|
|
1544
1577
|
private _buildDraftAndProxy({ data = {}, serverData = null, previousDraft = null, constant, apiClient, transforms = {}, throwOnError = true, removeFields = [] }: { data?: Record<string, unknown>; serverData?: Record<string, unknown> | null; previousDraft?: Record<string, unknown> | null; constant: string | string[]; apiClient: ApiClient; transforms?: TransformsMap; throwOnError?: boolean; removeFields?: string[] }): { draft: ReactiveData; proxy: Record<string, unknown>; initial: Record<string, unknown> } {
|
|
1545
1578
|
const constants = Array.isArray(constant) ? constant : [constant];
|
|
1546
|
-
const combinedSchema: {allOf: JsonSchema[], $defs: Record<string, JsonSchema>} = {
|
|
1579
|
+
const combinedSchema: { allOf: JsonSchema[], $defs: Record<string, JsonSchema> } = {
|
|
1547
1580
|
allOf: [],
|
|
1548
1581
|
$defs: {}
|
|
1549
1582
|
};
|
|
@@ -1601,7 +1634,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1601
1634
|
? transforms[key](raw, data)
|
|
1602
1635
|
: raw;
|
|
1603
1636
|
return [key, transformed];
|
|
1604
|
-
|
|
1637
|
+
|
|
1605
1638
|
}).filter(([_, v]) => v !== undefined)
|
|
1606
1639
|
);
|
|
1607
1640
|
|
|
@@ -1618,7 +1651,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1618
1651
|
|
|
1619
1652
|
const proxy = this._createDraftProxy(apiClient, reactiveServer, draft, allowed, transforms, { throwOnError });
|
|
1620
1653
|
|
|
1621
|
-
return { draft, proxy, initial};
|
|
1654
|
+
return { draft, proxy, initial };
|
|
1622
1655
|
}
|
|
1623
1656
|
|
|
1624
1657
|
/**
|
|
@@ -1844,20 +1877,20 @@ export class BaseEntity<TServerData = any> {
|
|
|
1844
1877
|
if (!this.id && this.slug) {
|
|
1845
1878
|
try {
|
|
1846
1879
|
const data = await this.endpointApi.getElementsKey({
|
|
1847
|
-
pathParams:{
|
|
1880
|
+
pathParams: {
|
|
1848
1881
|
slug: this.slug
|
|
1849
1882
|
}
|
|
1850
1883
|
}) as GetElementsKeyResponse;
|
|
1851
1884
|
|
|
1852
|
-
if(data.contextId && data.contextType === type) {
|
|
1885
|
+
if (data.contextId && data.contextType === type) {
|
|
1853
1886
|
this._id(data.contextId);
|
|
1854
1887
|
} else {
|
|
1855
1888
|
throw new ApiResponseError(`Le slug ${this.slug} ne correspond pas à un ${type}`, 200, data as object);
|
|
1856
1889
|
}
|
|
1857
1890
|
} catch (error) {
|
|
1858
|
-
if(error instanceof ApiResponseError) {
|
|
1891
|
+
if (error instanceof ApiResponseError) {
|
|
1859
1892
|
const errorResponseData = error.responseData as Record<string, unknown>;
|
|
1860
|
-
if(errorResponseData.contextType !== type) {
|
|
1893
|
+
if (errorResponseData.contextType !== type) {
|
|
1861
1894
|
throw error;
|
|
1862
1895
|
} else {
|
|
1863
1896
|
throw new ApiResponseError(`Impossible de récupérer l'identifiant pour le slug ${this.slug}`, error.status, error.responseData);
|
|
@@ -1872,7 +1905,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
1872
1905
|
}
|
|
1873
1906
|
return this.id;
|
|
1874
1907
|
}
|
|
1875
|
-
|
|
1908
|
+
|
|
1876
1909
|
/**
|
|
1877
1910
|
* Récupère le profil public de l'entité.
|
|
1878
1911
|
*
|
|
@@ -1887,9 +1920,9 @@ export class BaseEntity<TServerData = any> {
|
|
|
1887
1920
|
const type = this.getEntityType();
|
|
1888
1921
|
|
|
1889
1922
|
if (type === "news" || type === "comments") {
|
|
1890
|
-
throw new ApiError(
|
|
1923
|
+
throw new ApiError(`getElementsAbout ne supporte pas le type '${type}'.`, 400);
|
|
1891
1924
|
}
|
|
1892
|
-
return this.endpointApi.getElementsAbout({ pathParams: { id: this.id, type
|
|
1925
|
+
return this.endpointApi.getElementsAbout({ pathParams: { id: this.id, type }, tpl: "ficheInfoElement" });
|
|
1893
1926
|
}
|
|
1894
1927
|
|
|
1895
1928
|
/**
|
|
@@ -1913,24 +1946,28 @@ export class BaseEntity<TServerData = any> {
|
|
|
1913
1946
|
Comment: selfTag === "Comment" ? selfClass : this.deps.Comment,
|
|
1914
1947
|
Answer: selfTag === "Answer" ? selfClass : this.deps.Answer,
|
|
1915
1948
|
Form: selfTag === "Form" ? selfClass : this.deps.Form,
|
|
1949
|
+
Classified: selfTag === "Classified" ? selfClass : this.deps.Classified,
|
|
1916
1950
|
};
|
|
1917
1951
|
|
|
1918
1952
|
const map = {
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1953
|
+
classifieds: { entityClass: commonDeps.Classified, deps: { ...commonDeps } },
|
|
1954
|
+
citoyens: { entityClass: commonDeps.User, deps: commonDeps },
|
|
1955
|
+
organizations: { entityClass: commonDeps.Organization, deps: commonDeps },
|
|
1956
|
+
projects: { entityClass: commonDeps.Project, deps: commonDeps },
|
|
1957
|
+
events: { entityClass: commonDeps.Event, deps: { ...commonDeps, Badge: undefined } },
|
|
1958
|
+
poi: { entityClass: commonDeps.Poi, deps: { ...commonDeps, Badge: undefined, News: undefined } },
|
|
1959
|
+
news: { entityClass: commonDeps.News, deps: { ...commonDeps } },
|
|
1960
|
+
badges: {
|
|
1961
|
+
entityClass: commonDeps.Badge, deps: {
|
|
1962
|
+
EndpointApi: commonDeps.EndpointApi,
|
|
1963
|
+
User: commonDeps.User,
|
|
1964
|
+
Organization: commonDeps.Organization,
|
|
1965
|
+
Project: commonDeps.Project
|
|
1966
|
+
}
|
|
1967
|
+
},
|
|
1968
|
+
comments: { entityClass: commonDeps.Comment, deps: { ...commonDeps } },
|
|
1969
|
+
answers: { entityClass: commonDeps.Answer, deps: { ...commonDeps } },
|
|
1970
|
+
forms: { entityClass: commonDeps.Form, deps: { ...commonDeps } },
|
|
1934
1971
|
};
|
|
1935
1972
|
|
|
1936
1973
|
return (map as Record<string, EntityMeta | undefined>)[entityType] || null;
|
|
@@ -1980,11 +2017,11 @@ export class BaseEntity<TServerData = any> {
|
|
|
1980
2017
|
* @return L'entité liée ou null.
|
|
1981
2018
|
* @private
|
|
1982
2019
|
*/
|
|
1983
|
-
async _linkEntityById(entityType: string, entityId: string, options?: { skipGet?: boolean }): Promise<AnyEntity|null> {
|
|
2020
|
+
async _linkEntityById(entityType: string, entityId: string, options?: { skipGet?: boolean }): Promise<AnyEntity | null> {
|
|
1984
2021
|
const meta = this._getEntityMeta(entityType);
|
|
1985
2022
|
if (!meta) return null;
|
|
1986
2023
|
|
|
1987
|
-
|
|
2024
|
+
|
|
1988
2025
|
const parent: ParentLike = this as ParentLike;
|
|
1989
2026
|
|
|
1990
2027
|
const entity = new meta.entityClass(parent, { id: entityId }, meta.deps) as AnyEntity;
|
|
@@ -2162,6 +2199,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2162
2199
|
badges: ["id"],
|
|
2163
2200
|
comments: [], // Pas de get() pour les commentaires
|
|
2164
2201
|
answers: ["id"],
|
|
2202
|
+
classifieds: ["id"],
|
|
2165
2203
|
};
|
|
2166
2204
|
|
|
2167
2205
|
const fetchKeys = (fetchKeysByEntity as Record<string, string[] | undefined>)[entityType];
|
|
@@ -2190,12 +2228,12 @@ export class BaseEntity<TServerData = any> {
|
|
|
2190
2228
|
}
|
|
2191
2229
|
try {
|
|
2192
2230
|
const data = await this.endpointApi.getElementsKey({
|
|
2193
|
-
pathParams:{
|
|
2231
|
+
pathParams: {
|
|
2194
2232
|
slug: slug
|
|
2195
2233
|
}
|
|
2196
2234
|
}) as GetElementsKeyResponse;
|
|
2197
2235
|
|
|
2198
|
-
if(data.contextId && data.contextType) {
|
|
2236
|
+
if (data.contextId && data.contextType) {
|
|
2199
2237
|
const entity = await this.entity(data.contextType, { id: data.contextId });
|
|
2200
2238
|
|
|
2201
2239
|
// Corriger le userContext si nécessaire
|
|
@@ -2206,7 +2244,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2206
2244
|
throw new ApiResponseError(`Le slug ${slug} n'est pas valide`, 200, data as object);
|
|
2207
2245
|
}
|
|
2208
2246
|
} catch (error) {
|
|
2209
|
-
if(error instanceof ApiResponseError) {
|
|
2247
|
+
if (error instanceof ApiResponseError) {
|
|
2210
2248
|
throw new ApiResponseError(`Impossible de récupérer l'identifiant pour le slug ${slug}`, error.status, error.responseData);
|
|
2211
2249
|
} else {
|
|
2212
2250
|
throw error;
|
|
@@ -2351,7 +2389,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2351
2389
|
let hierarchyField: Record<string, any> | undefined;
|
|
2352
2390
|
if (entityType === "events") {
|
|
2353
2391
|
hierarchyField = entityData?.organizer;
|
|
2354
|
-
} else if (entityType === "projects" || entityType === "poi") {
|
|
2392
|
+
} else if (entityType === "projects" || entityType === "poi" || entityType === "classifieds") {
|
|
2355
2393
|
hierarchyField = entityData?.parent;
|
|
2356
2394
|
} else {
|
|
2357
2395
|
// Organizations et autres types n'ont pas de hiérarchie parent
|
|
@@ -2406,8 +2444,8 @@ export class BaseEntity<TServerData = any> {
|
|
|
2406
2444
|
|
|
2407
2445
|
const parentLink = parentLinks[parentId];
|
|
2408
2446
|
return this._validateUserLink(parentLink) &&
|
|
2409
|
-
|
|
2410
|
-
|
|
2447
|
+
parentLink?.isAdmin === true &&
|
|
2448
|
+
!parentLink?.isAdminPending;
|
|
2411
2449
|
}
|
|
2412
2450
|
|
|
2413
2451
|
/**
|
|
@@ -2476,7 +2514,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2476
2514
|
filters[`${path}.isAdmin`] = { $exists: true };
|
|
2477
2515
|
}
|
|
2478
2516
|
|
|
2479
|
-
if(isAdminPending === true) {
|
|
2517
|
+
if (isAdminPending === true) {
|
|
2480
2518
|
filters[`${path}.isAdminPending`] = { $exists: true };
|
|
2481
2519
|
}
|
|
2482
2520
|
|
|
@@ -2552,7 +2590,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2552
2590
|
*/
|
|
2553
2591
|
protected _getLinkFromConnectedUser() {
|
|
2554
2592
|
const { linkType } = this._getLinkMeta();
|
|
2555
|
-
if(!this.id) {
|
|
2593
|
+
if (!this.id) {
|
|
2556
2594
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
2557
2595
|
}
|
|
2558
2596
|
return this?.userContext?.serverData?.links?.[linkType]?.[this.id] || null;
|
|
@@ -2572,7 +2610,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2572
2610
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
2573
2611
|
}
|
|
2574
2612
|
|
|
2575
|
-
if(!this.userId) {
|
|
2613
|
+
if (!this.userId) {
|
|
2576
2614
|
throw new ApiError("L'utilisateur connecté n'est pas défini.", 400);
|
|
2577
2615
|
}
|
|
2578
2616
|
|
|
@@ -2759,7 +2797,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2759
2797
|
// Normalement en auto dans le schema mais je le met quand même
|
|
2760
2798
|
connectType: connectTypeDisconnect
|
|
2761
2799
|
};
|
|
2762
|
-
|
|
2800
|
+
|
|
2763
2801
|
// TODO : reflechir au moyen de remplir parent.serverData et this.serverData avec les data de retour pour eviter un refresh
|
|
2764
2802
|
const retour = await this.callIsMe(() => this.endpointApi.disconnect(data));
|
|
2765
2803
|
await this.userContext?.refresh();
|
|
@@ -2784,7 +2822,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2784
2822
|
return data;
|
|
2785
2823
|
});
|
|
2786
2824
|
}
|
|
2787
|
-
|
|
2825
|
+
|
|
2788
2826
|
/**
|
|
2789
2827
|
* Mettre à jour les paramètres d'un élément : Mise à jour des paramètres spécifiques d'un élément.
|
|
2790
2828
|
* Constant : UPDATE_SETTINGS
|
|
@@ -2819,7 +2857,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2819
2857
|
|
|
2820
2858
|
return this.callIsConnected(() => this.endpointApi.updateSettings(payload));
|
|
2821
2859
|
}
|
|
2822
|
-
|
|
2860
|
+
|
|
2823
2861
|
/**
|
|
2824
2862
|
* Mettre à jour la description d'un élément : Permet de mettre à jour la description courte et complète d'un élément.
|
|
2825
2863
|
* Constant : UPDATE_BLOCK_DESCRIPTION
|
|
@@ -2848,7 +2886,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2848
2886
|
|
|
2849
2887
|
return this.callIsConnected(() => this.endpointApi.updateBlockDescription(payload));
|
|
2850
2888
|
}
|
|
2851
|
-
|
|
2889
|
+
|
|
2852
2890
|
/**
|
|
2853
2891
|
* Mettre à jour les informations d'un élément : Permet de mettre à jour les informations générales d'un élément (nom, contacts, etc.).
|
|
2854
2892
|
* Constant : UPDATE_BLOCK_INFO
|
|
@@ -2876,7 +2914,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
2876
2914
|
};
|
|
2877
2915
|
return this.callIsConnected(() => this.endpointApi.updateBlockInfo(payload));
|
|
2878
2916
|
}
|
|
2879
|
-
|
|
2917
|
+
|
|
2880
2918
|
/**
|
|
2881
2919
|
* 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.
|
|
2882
2920
|
* Constant : UPDATE_BLOCK_SOCIAL
|
|
@@ -2924,9 +2962,9 @@ export class BaseEntity<TServerData = any> {
|
|
|
2924
2962
|
*/
|
|
2925
2963
|
private _isGeo(a: any): a is Geo {
|
|
2926
2964
|
return !!a
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2965
|
+
&& typeof a === "object"
|
|
2966
|
+
&& (typeof a.latitude === "number" || typeof a.latitude === "string")
|
|
2967
|
+
&& (typeof a.longitude === "number" || typeof a.longitude === "string");
|
|
2930
2968
|
}
|
|
2931
2969
|
|
|
2932
2970
|
/**
|
|
@@ -2934,13 +2972,13 @@ export class BaseEntity<TServerData = any> {
|
|
|
2934
2972
|
*/
|
|
2935
2973
|
private _isGeoPosition(a: any): a is GeoPosition {
|
|
2936
2974
|
return !!a
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2975
|
+
&& typeof a === "object"
|
|
2976
|
+
&& a.type === "Point"
|
|
2977
|
+
&& Array.isArray(a.coordinates)
|
|
2978
|
+
&& a.coordinates.length === 2
|
|
2979
|
+
&& (typeof a.coordinates[0] === "number" || typeof a.coordinates[0] === "string")
|
|
2980
|
+
&& (typeof a.coordinates[1] === "number" || typeof a.coordinates[1] === "string")
|
|
2981
|
+
&& a.float === true;
|
|
2944
2982
|
}
|
|
2945
2983
|
|
|
2946
2984
|
/**
|
|
@@ -2982,13 +3020,13 @@ export class BaseEntity<TServerData = any> {
|
|
|
2982
3020
|
typeElement,
|
|
2983
3021
|
address: addr,
|
|
2984
3022
|
// On ne recopie que les champs autorisés pour éviter de casser le typage
|
|
2985
|
-
...(
|
|
2986
|
-
...(
|
|
2987
|
-
...(
|
|
3023
|
+
...("geo" in data && this._isGeo(data.geo) ? { geo: data.geo } : {}),
|
|
3024
|
+
...("geoPosition" in data && this._isGeoPosition(data.geoPosition) ? { geoPosition: data.geoPosition } : {}),
|
|
3025
|
+
...("locality" in data ? { locality: data.locality } : {}),
|
|
2988
3026
|
};
|
|
2989
3027
|
return this.callIsConnected(() => this.endpointApi.updateBlockLocality(payload));
|
|
2990
3028
|
}
|
|
2991
|
-
|
|
3029
|
+
|
|
2992
3030
|
/**
|
|
2993
3031
|
* Mettre à jour le slug d'un élément : Permet de mettre à jour le slug pour une URL simplifiée.
|
|
2994
3032
|
* Constant : UPDATE_BLOCK_SLUG
|
|
@@ -3009,7 +3047,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3009
3047
|
try {
|
|
3010
3048
|
await this.endpointApi.check({ block: "info", type, id: this.id, slug });
|
|
3011
3049
|
} catch (error) {
|
|
3012
|
-
if(error instanceof ApiResponseError) {
|
|
3050
|
+
if (error instanceof ApiResponseError) {
|
|
3013
3051
|
throw new ApiResponseError("Erreur lors de la vérification du slug.", error.status, error.responseData);
|
|
3014
3052
|
}
|
|
3015
3053
|
throw error;
|
|
@@ -3027,7 +3065,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3027
3065
|
* @param data.profil_avatar - L'image de profil à mettre à jour.
|
|
3028
3066
|
* @returns - Les données de réponse.
|
|
3029
3067
|
*/
|
|
3030
|
-
async updateImageProfil({ profil_avatar: image
|
|
3068
|
+
async updateImageProfil({ profil_avatar: image }: { profil_avatar: UploadInput }): Promise<unknown> {
|
|
3031
3069
|
|
|
3032
3070
|
if (!this.id) {
|
|
3033
3071
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
@@ -3039,7 +3077,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3039
3077
|
|
|
3040
3078
|
image = await this._validateImage(image);
|
|
3041
3079
|
|
|
3042
|
-
const data: ProfilImageData = { pathParams: { folder, ownerId: this.id },
|
|
3080
|
+
const data: ProfilImageData = { pathParams: { folder, ownerId: this.id }, profil_avatar: image as unknown as Record<string, unknown>, };
|
|
3043
3081
|
return this.callIsConnected(() => this.endpointApi.profilImage(data));
|
|
3044
3082
|
}
|
|
3045
3083
|
|
|
@@ -3054,7 +3092,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3054
3092
|
* @param data.cropY - Position Y du recadrage.
|
|
3055
3093
|
* @returns - Les données de réponse.
|
|
3056
3094
|
*/
|
|
3057
|
-
async updateImageBanner({ banner: image, cropW, cropH, cropX, cropY
|
|
3095
|
+
async updateImageBanner({ banner: image, cropW, cropH, cropX, cropY }: { banner: UploadInput, cropW: number, cropH: number, cropX: number, cropY: number }): Promise<unknown> {
|
|
3058
3096
|
|
|
3059
3097
|
if (!this.id) {
|
|
3060
3098
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
@@ -3080,7 +3118,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3080
3118
|
* @throws {Error} Si une erreur se produit lors de la création de l'organisation.
|
|
3081
3119
|
*/
|
|
3082
3120
|
async organization(organizationData: OrganizationInput = {}): Promise<Organization> {
|
|
3083
|
-
if(!("id" in organizationData) && !("slug" in organizationData) && !this.isMe){
|
|
3121
|
+
if (!("id" in organizationData) && !("slug" in organizationData) && !this.isMe) {
|
|
3084
3122
|
throw new ApiError("Vous devez être connecté et être l'utilisateur pour créer une organisation.", 403);
|
|
3085
3123
|
}
|
|
3086
3124
|
const entity = await this.entity("organizations", organizationData);
|
|
@@ -3098,7 +3136,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3098
3136
|
*/
|
|
3099
3137
|
async project(projectData: ProjectInput = {}): Promise<Project> {
|
|
3100
3138
|
// TODO: Vérifier si l'utilisateur est admin de l'organisation
|
|
3101
|
-
if(!("id" in projectData) && !("slug" in projectData) && !this.isConnected){
|
|
3139
|
+
if (!("id" in projectData) && !("slug" in projectData) && !this.isConnected) {
|
|
3102
3140
|
throw new ApiError("Vous devez être connecté pour créer un projet.", 401);
|
|
3103
3141
|
}
|
|
3104
3142
|
|
|
@@ -3117,7 +3155,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3117
3155
|
*/
|
|
3118
3156
|
async poi(poiData: PoiInput = {}): Promise<Poi> {
|
|
3119
3157
|
// TODO: Vérifier si l'utilisateur est admin de l'organisation
|
|
3120
|
-
if(!("id" in poiData) && !("slug" in poiData) && !this.isConnected){
|
|
3158
|
+
if (!("id" in poiData) && !("slug" in poiData) && !this.isConnected) {
|
|
3121
3159
|
throw new ApiError("Vous devez être connecté pour créer un POI.", 401);
|
|
3122
3160
|
}
|
|
3123
3161
|
|
|
@@ -3125,6 +3163,24 @@ export class BaseEntity<TServerData = any> {
|
|
|
3125
3163
|
return entity as Poi;
|
|
3126
3164
|
}
|
|
3127
3165
|
|
|
3166
|
+
/**
|
|
3167
|
+
* Crée une instance de ressource classifiée (besoin ou offre).
|
|
3168
|
+
*
|
|
3169
|
+
* @param classifiedData - Les données pour initialiser la ressource.
|
|
3170
|
+
* - Si { id } : récupère la ressource existante (GET)
|
|
3171
|
+
* - Sinon : crée une nouvelle instance (CREATE)
|
|
3172
|
+
* @returns Une promesse qui résout l'objet Classified créé.
|
|
3173
|
+
* @throws {Error} Si une erreur se produit lors de la création.
|
|
3174
|
+
*/
|
|
3175
|
+
async classified(classifiedData: ClassifiedInput = {}): Promise<Classified> {
|
|
3176
|
+
if (!("id" in classifiedData) && !this.isConnected) {
|
|
3177
|
+
throw new ApiError("Vous devez être connecté pour créer une ressource.", 401);
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
const entity = await this.entity("classifieds", classifiedData);
|
|
3181
|
+
return entity as Classified;
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3128
3184
|
/**
|
|
3129
3185
|
* Crée une instance d'événement et la récupère si nécessaire.
|
|
3130
3186
|
*
|
|
@@ -3136,7 +3192,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3136
3192
|
*/
|
|
3137
3193
|
async event(eventData: EventInput = {}): Promise<EventEntity> {
|
|
3138
3194
|
// TODO: Vérifier si l'utilisateur est admin de l'organisation
|
|
3139
|
-
if(!("id" in eventData) && !("slug" in eventData) && !this.isConnected){
|
|
3195
|
+
if (!("id" in eventData) && !("slug" in eventData) && !this.isConnected) {
|
|
3140
3196
|
throw new ApiError("Vous devez être connecté pour créer un événement.", 401);
|
|
3141
3197
|
}
|
|
3142
3198
|
const entity = await this.entity("events", eventData);
|
|
@@ -3154,7 +3210,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3154
3210
|
*/
|
|
3155
3211
|
async badge(badgeData: BadgeInput = {}): Promise<Badge> {
|
|
3156
3212
|
// TODO: Vérifier si l'utilisateur est admin de l'organisation
|
|
3157
|
-
if(!("id" in badgeData) && !this.isConnected){
|
|
3213
|
+
if (!("id" in badgeData) && !this.isConnected) {
|
|
3158
3214
|
throw new ApiError("Vous devez être connecté pour créer un badge.", 401);
|
|
3159
3215
|
}
|
|
3160
3216
|
const entity = await this.entity("badges", badgeData);
|
|
@@ -3171,7 +3227,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3171
3227
|
* @throws {Error} Si une erreur se produit lors de la création de la news.
|
|
3172
3228
|
*/
|
|
3173
3229
|
async news(newsData: NewsInput = {}): Promise<News> {
|
|
3174
|
-
if(!newsData?.id && !this.isConnected){
|
|
3230
|
+
if (!newsData?.id && !this.isConnected) {
|
|
3175
3231
|
throw new ApiError("Vous devez être connecté.", 401);
|
|
3176
3232
|
}
|
|
3177
3233
|
|
|
@@ -3197,14 +3253,14 @@ export class BaseEntity<TServerData = any> {
|
|
|
3197
3253
|
methodName: "getOrganizations",
|
|
3198
3254
|
restoredState: options?.restoredState,
|
|
3199
3255
|
finalizer: async (finalData) => {
|
|
3200
|
-
if(this.isMe){
|
|
3256
|
+
if (this.isMe) {
|
|
3201
3257
|
finalData.pathParams = { type: this.getEntityType(), id: this.id };
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3258
|
+
// NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
|
|
3259
|
+
// finalData.filters = {
|
|
3260
|
+
// [`links.members.${this.id}`]: { "$exists": true },
|
|
3261
|
+
// [`links.members.${this.id}.toBeValidated`]: { "$exists": false },
|
|
3262
|
+
// [`links.members.${this.id}.isInviting`]: { "$exists": false }
|
|
3263
|
+
// };
|
|
3208
3264
|
} else {
|
|
3209
3265
|
delete finalData?.pathParams;
|
|
3210
3266
|
finalData.filters = {
|
|
@@ -3213,7 +3269,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3213
3269
|
[`links.members.${this.id}.isInviting`]: { "$exists": false }
|
|
3214
3270
|
};
|
|
3215
3271
|
}
|
|
3216
|
-
|
|
3272
|
+
|
|
3217
3273
|
const fetchFn = this.isMe
|
|
3218
3274
|
? () => this.callIsMe(() => this.endpointApi.getOrganizationsAdmin(finalData))
|
|
3219
3275
|
: () => this.endpointApi.getOrganizationsNoAdmin(finalData);
|
|
@@ -3243,7 +3299,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3243
3299
|
methodName: "getProjects",
|
|
3244
3300
|
restoredState: options?.restoredState,
|
|
3245
3301
|
finalizer: async (finalData) => {
|
|
3246
|
-
if(this.isMe){
|
|
3302
|
+
if (this.isMe) {
|
|
3247
3303
|
finalData.pathParams = { type: this.getEntityType(), id: this.id };
|
|
3248
3304
|
finalData.filters = {
|
|
3249
3305
|
"$or": {
|
|
@@ -3262,7 +3318,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3262
3318
|
[`links.contributors.${this.id}`]: { "$exists": true }
|
|
3263
3319
|
};
|
|
3264
3320
|
}
|
|
3265
|
-
|
|
3321
|
+
|
|
3266
3322
|
const fetchFn = this.isMe
|
|
3267
3323
|
? () => this.callIsMe(() => this.endpointApi.getProjectsAdmin(finalData))
|
|
3268
3324
|
: () => this.endpointApi.getProjectsNoAdmin(finalData);
|
|
@@ -3323,7 +3379,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3323
3379
|
methodName: "getPois",
|
|
3324
3380
|
restoredState: options?.restoredState,
|
|
3325
3381
|
finalizer: async (finalData) => {
|
|
3326
|
-
if(this.isMe){
|
|
3382
|
+
if (this.isMe) {
|
|
3327
3383
|
finalData.pathParams = { type: this.getEntityType(), id: this.id };
|
|
3328
3384
|
// NOTE : dans le schema je crois que si pas de finalData.filters alors le default ce fait avec finalData.pathParams
|
|
3329
3385
|
// finalData.filters = {
|
|
@@ -3335,7 +3391,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3335
3391
|
[`parent.${this.id}`]: { "$exists": true },
|
|
3336
3392
|
};
|
|
3337
3393
|
}
|
|
3338
|
-
|
|
3394
|
+
|
|
3339
3395
|
const fetchFn = this.isMe
|
|
3340
3396
|
? () => this.callIsMe(() => this.endpointApi.getPoisAdmin(finalData))
|
|
3341
3397
|
: () => this.endpointApi.getPoisNoAdmin(finalData);
|
|
@@ -3367,7 +3423,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3367
3423
|
finalizer: async (finalData) => {
|
|
3368
3424
|
delete finalData?.pathParams;
|
|
3369
3425
|
|
|
3370
|
-
finalData.filters =
|
|
3426
|
+
finalData.filters = {
|
|
3371
3427
|
[`links.follows.${this.id}`]: { "$exists": true },
|
|
3372
3428
|
[`links.follows.${this.id}.toBeValidated`]: { "$exists": false },
|
|
3373
3429
|
[`links.follows.${this.id}.isInviting`]: { "$exists": false }
|
|
@@ -3399,7 +3455,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3399
3455
|
restoredState: options?.restoredState,
|
|
3400
3456
|
finalizer: async (finalData) => {
|
|
3401
3457
|
delete finalData?.pathParams;
|
|
3402
|
-
|
|
3458
|
+
|
|
3403
3459
|
finalData.filters = finalData.filters || { "preferences.private": false };
|
|
3404
3460
|
finalData.filters["$or"] = {};
|
|
3405
3461
|
finalData.filters["$or"][`issuer.${this.id}`] = { "$exists": true };
|
|
@@ -3440,8 +3496,8 @@ export class BaseEntity<TServerData = any> {
|
|
|
3440
3496
|
...search
|
|
3441
3497
|
};
|
|
3442
3498
|
|
|
3443
|
-
const arrayObjet =
|
|
3444
|
-
if(!Array.isArray(arrayObjet)){
|
|
3499
|
+
const arrayObjet = await this.endpointApi.getNews(payload);
|
|
3500
|
+
if (!Array.isArray(arrayObjet)) {
|
|
3445
3501
|
throw new ApiResponseError("Erreur lors de la récupération des actualités.", 500, arrayObjet as object);
|
|
3446
3502
|
}
|
|
3447
3503
|
|
|
@@ -3472,7 +3528,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3472
3528
|
pathParams: { type, id: this.id, docType: data.pathParams?.docType || "image" }
|
|
3473
3529
|
};
|
|
3474
3530
|
|
|
3475
|
-
const arrayObjet =
|
|
3531
|
+
const arrayObjet = await this.endpointApi.getGallery(payload);
|
|
3476
3532
|
|
|
3477
3533
|
return arrayObjet as Record<string, any>;
|
|
3478
3534
|
}
|
|
@@ -3530,7 +3586,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3530
3586
|
const rawList = this._linkEntities(result);
|
|
3531
3587
|
return rawList;
|
|
3532
3588
|
}
|
|
3533
|
-
|
|
3589
|
+
|
|
3534
3590
|
/**
|
|
3535
3591
|
* Soumet une demande pour rejoindre l'entité courante (ex. organisation, projet, événement...).
|
|
3536
3592
|
* Si une invitation est en attente, elle est automatiquement acceptée.
|
|
@@ -3581,7 +3637,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3581
3637
|
}
|
|
3582
3638
|
|
|
3583
3639
|
this._checkLinkableEntity();
|
|
3584
|
-
|
|
3640
|
+
|
|
3585
3641
|
if (!this.id) {
|
|
3586
3642
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3587
3643
|
}
|
|
@@ -3673,7 +3729,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3673
3729
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3674
3730
|
}
|
|
3675
3731
|
|
|
3676
|
-
if(!this.userId) {
|
|
3732
|
+
if (!this.userId) {
|
|
3677
3733
|
throw new ApiError("Utilisateur non connecté.", 401);
|
|
3678
3734
|
}
|
|
3679
3735
|
|
|
@@ -3715,7 +3771,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3715
3771
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3716
3772
|
}
|
|
3717
3773
|
|
|
3718
|
-
if(!this.userId) {
|
|
3774
|
+
if (!this.userId) {
|
|
3719
3775
|
throw new ApiError("Utilisateur non connecté.", 401);
|
|
3720
3776
|
}
|
|
3721
3777
|
|
|
@@ -3741,7 +3797,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3741
3797
|
|
|
3742
3798
|
throw new ApiError("Vous n'êtes pas abonné à cette entité.", 404);
|
|
3743
3799
|
}
|
|
3744
|
-
|
|
3800
|
+
|
|
3745
3801
|
|
|
3746
3802
|
/**
|
|
3747
3803
|
* Vérifie si l'utilisateur est connecté et a accès à l'entité.
|
|
@@ -3840,7 +3896,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3840
3896
|
* @protected
|
|
3841
3897
|
*/
|
|
3842
3898
|
protected _isLinked(linkType: string): boolean {
|
|
3843
|
-
if(!this.id) {
|
|
3899
|
+
if (!this.id) {
|
|
3844
3900
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
3845
3901
|
}
|
|
3846
3902
|
const links = this.userContext?.serverData?.links;
|
|
@@ -3894,7 +3950,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
3894
3950
|
isAdmin(options?: { checkHierarchy?: boolean; silent?: boolean }): boolean {
|
|
3895
3951
|
const userLink = this._getValidatedUserLink(
|
|
3896
3952
|
"vérifier l'administrateur",
|
|
3897
|
-
["organizations", "projects", "events"],
|
|
3953
|
+
["organizations", "projects", "events", "classifieds"],
|
|
3898
3954
|
{ silent: options?.silent }
|
|
3899
3955
|
);
|
|
3900
3956
|
|
|
@@ -4286,12 +4342,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4286
4342
|
* Injection de contexte Communecter dans une requête finalizer.
|
|
4287
4343
|
*/
|
|
4288
4344
|
_withCostumContext<TIn extends Record<string, unknown>, TOut>(
|
|
4289
|
-
baseFinalizer: (data: TIn &
|
|
4290
|
-
costumSlug: string,
|
|
4291
|
-
contextId: string,
|
|
4292
|
-
contextType: EntityType,
|
|
4293
|
-
sourceKey: string[]
|
|
4294
|
-
}) => Promise<TOut>
|
|
4345
|
+
baseFinalizer: (data: TIn & CostumContextFields) => Promise<TOut>
|
|
4295
4346
|
): (data: TIn) => Promise<TOut> {
|
|
4296
4347
|
return async (data: TIn) => {
|
|
4297
4348
|
if (!this.serverData) {
|
|
@@ -4317,12 +4368,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4317
4368
|
contextId: sd.id,
|
|
4318
4369
|
contextType: this.getEntityType(),
|
|
4319
4370
|
sourceKey: [...inSourceKey, sd.slug] as string[]
|
|
4320
|
-
} as TIn &
|
|
4321
|
-
costumSlug: string;
|
|
4322
|
-
contextId: string;
|
|
4323
|
-
contextType: EntityType;
|
|
4324
|
-
sourceKey: string[];
|
|
4325
|
-
};
|
|
4371
|
+
} as TIn & CostumContextFields;
|
|
4326
4372
|
|
|
4327
4373
|
return baseFinalizer(finalData);
|
|
4328
4374
|
};
|
|
@@ -4390,7 +4436,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4390
4436
|
*/
|
|
4391
4437
|
async costumEventRequestActors(data: Partial<Omit<CostumEventRequestActorsData, "pathParams">> = {}): Promise<unknown> {
|
|
4392
4438
|
|
|
4393
|
-
if(!this.id) {
|
|
4439
|
+
if (!this.id) {
|
|
4394
4440
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4395
4441
|
}
|
|
4396
4442
|
|
|
@@ -4438,7 +4484,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4438
4484
|
*/
|
|
4439
4485
|
async costumEventRequestSubevents(data: Partial<Omit<CostumEventRequestSubeventsData, "pathParams">> = {}): Promise<unknown> {
|
|
4440
4486
|
|
|
4441
|
-
if(!this.id) {
|
|
4487
|
+
if (!this.id) {
|
|
4442
4488
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4443
4489
|
}
|
|
4444
4490
|
|
|
@@ -4468,7 +4514,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4468
4514
|
*/
|
|
4469
4515
|
async costumEventRequestDates(data: Partial<Omit<CostumEventRequestDatesData, "pathParams">> = {}): Promise<unknown> {
|
|
4470
4516
|
|
|
4471
|
-
if(!this.id) {
|
|
4517
|
+
if (!this.id) {
|
|
4472
4518
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4473
4519
|
}
|
|
4474
4520
|
|
|
@@ -4498,7 +4544,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4498
4544
|
*/
|
|
4499
4545
|
async costumEventRequestElementEvent(data: Partial<Omit<CostumEventRequestElementEventData, "pathParams">> = {}): Promise<unknown> {
|
|
4500
4546
|
|
|
4501
|
-
if(!this.id) {
|
|
4547
|
+
if (!this.id) {
|
|
4502
4548
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4503
4549
|
}
|
|
4504
4550
|
|
|
@@ -4528,7 +4574,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4528
4574
|
*/
|
|
4529
4575
|
async costumEventRequestCategories(data: Partial<Omit<CostumEventRequestCategoriesData, "pathParams">> = {}): Promise<unknown> {
|
|
4530
4576
|
|
|
4531
|
-
if(!this.id) {
|
|
4577
|
+
if (!this.id) {
|
|
4532
4578
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4533
4579
|
}
|
|
4534
4580
|
|
|
@@ -4558,7 +4604,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4558
4604
|
*/
|
|
4559
4605
|
async costumEventRequestEvent(data: Partial<Omit<CostumEventRequestEventData, "pathParams">> = {}): Promise<unknown> {
|
|
4560
4606
|
|
|
4561
|
-
if(!this.id) {
|
|
4607
|
+
if (!this.id) {
|
|
4562
4608
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4563
4609
|
}
|
|
4564
4610
|
|
|
@@ -4588,18 +4634,18 @@ export class BaseEntity<TServerData = any> {
|
|
|
4588
4634
|
*/
|
|
4589
4635
|
async costumEventRequestLinkTlToEvent(
|
|
4590
4636
|
data: Pick<CostumEventRequestLinkTlToEventData, "tl" | "event"> &
|
|
4591
|
-
|
|
4637
|
+
Partial<Omit<CostumEventRequestLinkTlToEventData, "tl" | "event" | "pathParams">>
|
|
4592
4638
|
): Promise<unknown> {
|
|
4593
4639
|
|
|
4594
|
-
if(!this.id) {
|
|
4640
|
+
if (!this.id) {
|
|
4595
4641
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4596
4642
|
}
|
|
4597
4643
|
|
|
4598
|
-
if(!data.tl && !data.event) {
|
|
4644
|
+
if (!data.tl && !data.event) {
|
|
4599
4645
|
throw new ApiError("Les paramètres 'tl' et 'event' sont requis.", 400);
|
|
4600
4646
|
}
|
|
4601
4647
|
|
|
4602
|
-
if(typeof data.tl !== "string" || typeof data.event !== "string") {
|
|
4648
|
+
if (typeof data.tl !== "string" || typeof data.event !== "string") {
|
|
4603
4649
|
throw new ApiError("Les paramètres 'tl' et 'event' doivent être des chaînes de caractères.", 400);
|
|
4604
4650
|
}
|
|
4605
4651
|
|
|
@@ -4629,7 +4675,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4629
4675
|
*/
|
|
4630
4676
|
async costumEventRequestLoadContextTag(data: Partial<Omit<CostumEventRequestLoadContextTagData, "pathParams">> = {}): Promise<unknown> {
|
|
4631
4677
|
|
|
4632
|
-
if(!this.id) {
|
|
4678
|
+
if (!this.id) {
|
|
4633
4679
|
throw new ApiError(`${this.constructor.name} non enregistrée.`, 404);
|
|
4634
4680
|
}
|
|
4635
4681
|
|
|
@@ -4648,7 +4694,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4648
4694
|
return wrappedFinalizer(fullData);
|
|
4649
4695
|
}
|
|
4650
4696
|
|
|
4651
|
-
|
|
4697
|
+
|
|
4652
4698
|
/**
|
|
4653
4699
|
* Recherche paginée des réponses CoForm liées à l'entité courante.
|
|
4654
4700
|
* Injecte automatiquement le contexte (costumSlug, contextId, contextType) via `_withCostumContext`,
|
|
@@ -4714,8 +4760,8 @@ export class BaseEntity<TServerData = any> {
|
|
|
4714
4760
|
*/
|
|
4715
4761
|
async searchAnswersByForms(
|
|
4716
4762
|
data: Partial<CoformAnswersByFormsData> = {},
|
|
4717
|
-
): Promise<{ answers: Answer[]; documents: any[];
|
|
4718
|
-
if(!data.forms || Array.isArray(data.forms) || Object.keys(data.forms).length === 0){
|
|
4763
|
+
): Promise<{ answers: Answer[]; documents: any[];[k: string]: unknown }[]> {
|
|
4764
|
+
if (!data.forms || Array.isArray(data.forms) || Object.keys(data.forms).length === 0) {
|
|
4719
4765
|
throw new ApiError("Le paramètre 'forms' est requis et doit être un objet non vide.", 400);
|
|
4720
4766
|
}
|
|
4721
4767
|
const result = await this.endpointApi.coformAnswersByForms(data);
|
|
@@ -4761,7 +4807,20 @@ export class BaseEntity<TServerData = any> {
|
|
|
4761
4807
|
...(result.contextData && { contextData: this._linkEntity(result.contextData.collection, result.contextData) ?? result.contextData }),
|
|
4762
4808
|
...(result.context && { context: this._linkEntity(result.context.collection, result.context) ?? result.context }),
|
|
4763
4809
|
...(result.nopropProject && { nopropProject: this._linkEntities(Object.values(result.nopropProject)) ?? result.nopropProject }),
|
|
4764
|
-
|
|
4810
|
+
// `result.projects` est un tableau d'enveloppes — la vraie entité projet est à
|
|
4811
|
+
// `result.projects[i].project`, pas à la racine de l'item (qui ne porte que les
|
|
4812
|
+
// métadonnées de financement). On linke chaque sous-champ `project` individuellement
|
|
4813
|
+
// pour récupérer des instances d'entités réactives, tout en préservant les méta.
|
|
4814
|
+
...(result.projects && {
|
|
4815
|
+
projects: (result.projects as FundingEnvelopeProjectItem[]).map((envelope) => {
|
|
4816
|
+
const inner = envelope?.project as { collection?: string } | undefined;
|
|
4817
|
+
if (inner && typeof inner === "object") {
|
|
4818
|
+
const linked = this._linkEntity(inner.collection ?? "projects", inner);
|
|
4819
|
+
return { ...envelope, project: linked ?? envelope.project };
|
|
4820
|
+
}
|
|
4821
|
+
return envelope;
|
|
4822
|
+
})
|
|
4823
|
+
}),
|
|
4765
4824
|
...(result.userOrga && { userOrga: this._linkEntities(Object.values(result.userOrga)) ?? result.userOrga })
|
|
4766
4825
|
};
|
|
4767
4826
|
}
|
|
@@ -4814,7 +4873,7 @@ export class BaseEntity<TServerData = any> {
|
|
|
4814
4873
|
async searchZone(
|
|
4815
4874
|
data: SearchZonesData,
|
|
4816
4875
|
): Promise<ZoneItemNormalized[]> {
|
|
4817
|
-
if(!data.countryCode?.length || !data.level?.length){
|
|
4876
|
+
if (!data.countryCode?.length || !data.level?.length) {
|
|
4818
4877
|
throw new ApiError("countryCode et level sont requis.", 400);
|
|
4819
4878
|
}
|
|
4820
4879
|
const wrappedFinalizer = this._withCostumContext(
|
|
@@ -4854,10 +4913,286 @@ export class BaseEntity<TServerData = any> {
|
|
|
4854
4913
|
if (!formId || typeof formId !== "string") {
|
|
4855
4914
|
throw new ApiError("formId est requis et doit être une chaîne de caractères.", 400);
|
|
4856
4915
|
}
|
|
4857
|
-
const result = await this.endpointApi.generateAnswerFromForm({pathParams: { formId }, action: "new" });
|
|
4916
|
+
const result = await this.endpointApi.generateAnswerFromForm({ pathParams: { formId }, action: "new" });
|
|
4858
4917
|
return this._linkEntity?.(result.collection, result) ?? result;
|
|
4859
4918
|
}
|
|
4860
4919
|
|
|
4920
|
+
/**
|
|
4921
|
+
* Associe un compte Discourse à l'utilisateur courant dans le contexte de l'instance.
|
|
4922
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
4923
|
+
*
|
|
4924
|
+
* @param username - Nom d'utilisateur Discourse à associer
|
|
4925
|
+
* @returns Résultat de la liaison avec les informations du compte Discourse trouvé
|
|
4926
|
+
* @throws {ApiError} Si `username` est absent ou invalide
|
|
4927
|
+
*
|
|
4928
|
+
* @example
|
|
4929
|
+
* const result = await user.linkDiscourseAccount("john_doe");
|
|
4930
|
+
* if (result.result) {
|
|
4931
|
+
* console.log(result.profileUrl); // URL du profil Discourse
|
|
4932
|
+
* } else {
|
|
4933
|
+
* console.error(result.error);
|
|
4934
|
+
* }
|
|
4935
|
+
*/
|
|
4936
|
+
async linkDiscourseAccount(
|
|
4937
|
+
username: string,
|
|
4938
|
+
): Promise<{
|
|
4939
|
+
result: boolean,
|
|
4940
|
+
error?: string
|
|
4941
|
+
username?: string
|
|
4942
|
+
profileUrl?: string
|
|
4943
|
+
}> {
|
|
4944
|
+
if (!username || typeof username !== "string") {
|
|
4945
|
+
throw new ApiError("username est requis et doit être une chaîne de caractères.", 400);
|
|
4946
|
+
}
|
|
4947
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
4948
|
+
(finalData: WithCostumContext<{ username: string }>) =>
|
|
4949
|
+
this.endpointApi.linkDiscourseAccount({
|
|
4950
|
+
...finalData,
|
|
4951
|
+
costumId: finalData.contextId,
|
|
4952
|
+
costumType: finalData.contextType,
|
|
4953
|
+
} as LinkDiscourseAccountData)
|
|
4954
|
+
);
|
|
4955
|
+
return wrappedFinalizer({ username });
|
|
4956
|
+
}
|
|
4957
|
+
|
|
4958
|
+
/**
|
|
4959
|
+
* Dissocie le compte Discourse de l'utilisateur courant dans le contexte de l'instance.
|
|
4960
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
4961
|
+
*
|
|
4962
|
+
* @returns Résultat du délien du compte Discourse
|
|
4963
|
+
*
|
|
4964
|
+
* @example
|
|
4965
|
+
* const result = await user.unlinkDiscourseAccount();
|
|
4966
|
+
* result.result; // true si succès
|
|
4967
|
+
*/
|
|
4968
|
+
async unlinkDiscourseAccount(): Promise<{
|
|
4969
|
+
result: boolean;
|
|
4970
|
+
error?: string;
|
|
4971
|
+
}> {
|
|
4972
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
4973
|
+
(finalData: WithCostumContext<Record<string, never>>) =>
|
|
4974
|
+
this.endpointApi.unlinkDiscourseAccount({
|
|
4975
|
+
...finalData,
|
|
4976
|
+
costumId: finalData.contextId,
|
|
4977
|
+
costumType: finalData.contextType,
|
|
4978
|
+
} as UnlinkDiscourseAccountData)
|
|
4979
|
+
);
|
|
4980
|
+
return wrappedFinalizer({});
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4983
|
+
/**
|
|
4984
|
+
* Récupère le profil Discourse d'un utilisateur dans le contexte de l'instance.
|
|
4985
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
4986
|
+
*
|
|
4987
|
+
* @param username - Nom d'utilisateur Discourse cible
|
|
4988
|
+
* @returns Profil Discourse avec `summary` et `profileUrl`, ou `error` en cas d'échec
|
|
4989
|
+
* @throws {ApiError} Si `username` est absent ou invalide
|
|
4990
|
+
*
|
|
4991
|
+
* @example
|
|
4992
|
+
* const result = await user.getDiscourseProfile("john_doe");
|
|
4993
|
+
* result.profileUrl; // "https://forum.example.org/u/john_doe/summary"
|
|
4994
|
+
*/
|
|
4995
|
+
async getDiscourseProfile(
|
|
4996
|
+
username: string,
|
|
4997
|
+
): Promise<{
|
|
4998
|
+
summary?: Record<string, unknown>;
|
|
4999
|
+
profileUrl?: string;
|
|
5000
|
+
error?: string;
|
|
5001
|
+
}> {
|
|
5002
|
+
if (!username || typeof username !== "string") {
|
|
5003
|
+
throw new ApiError("username est requis et doit être une chaîne de caractères.", 400);
|
|
5004
|
+
}
|
|
5005
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5006
|
+
(finalData: WithCostumContext<{ username: string }>) =>
|
|
5007
|
+
this.endpointApi.discourseProfile({
|
|
5008
|
+
...finalData,
|
|
5009
|
+
costumId: finalData.contextId,
|
|
5010
|
+
costumType: finalData.contextType,
|
|
5011
|
+
} as DiscourseProfileData)
|
|
5012
|
+
);
|
|
5013
|
+
return wrappedFinalizer({ username });
|
|
5014
|
+
}
|
|
5015
|
+
|
|
5016
|
+
/**
|
|
5017
|
+
* Vérifie si l'email de l'utilisateur connecté correspond à un compte Discourse.
|
|
5018
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
5019
|
+
* L'email utilisé est celui de l'utilisateur connecté (géré côté serveur).
|
|
5020
|
+
*
|
|
5021
|
+
* @returns `{ found: true, user }` si un compte correspondant existe, `{ found: false }` sinon
|
|
5022
|
+
*
|
|
5023
|
+
* @example
|
|
5024
|
+
* const result = await user.checkDiscourseEmailMatch();
|
|
5025
|
+
* if (result.found) {
|
|
5026
|
+
* console.log(result.user); // infos du compte Discourse trouvé
|
|
5027
|
+
* }
|
|
5028
|
+
*/
|
|
5029
|
+
async checkDiscourseEmailMatch(): Promise<{
|
|
5030
|
+
found: boolean;
|
|
5031
|
+
user?: Record<string, unknown>;
|
|
5032
|
+
}> {
|
|
5033
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5034
|
+
(finalData: WithCostumContext<Record<string, never>>) =>
|
|
5035
|
+
this.endpointApi.discourseCheckEmail({
|
|
5036
|
+
...finalData,
|
|
5037
|
+
costumId: finalData.contextId,
|
|
5038
|
+
costumType: finalData.contextType,
|
|
5039
|
+
} as DiscourseCheckEmailData)
|
|
5040
|
+
);
|
|
5041
|
+
return wrappedFinalizer({});
|
|
5042
|
+
}
|
|
5043
|
+
|
|
5044
|
+
/**
|
|
5045
|
+
* Ignore la suggestion de liaison de compte Discourse pour l'instance courante.
|
|
5046
|
+
* Persiste le refus côté serveur (`interop.discourse[costumSlug] = false`).
|
|
5047
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
5048
|
+
*
|
|
5049
|
+
* @returns `{ result: true }` si le dismiss a réussi
|
|
5050
|
+
*
|
|
5051
|
+
* @example
|
|
5052
|
+
* const result = await user.dismissDiscourseLink();
|
|
5053
|
+
* result.result; // true
|
|
5054
|
+
*/
|
|
5055
|
+
async dismissDiscourseLink(): Promise<{
|
|
5056
|
+
result: boolean;
|
|
5057
|
+
error?: string;
|
|
5058
|
+
}> {
|
|
5059
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5060
|
+
(finalData: WithCostumContext<Record<string, never>>) =>
|
|
5061
|
+
this.endpointApi.discourseDismissLink({
|
|
5062
|
+
...finalData,
|
|
5063
|
+
costumId: finalData.contextId,
|
|
5064
|
+
costumType: finalData.contextType,
|
|
5065
|
+
} as DiscourseDismissLinkData)
|
|
5066
|
+
);
|
|
5067
|
+
return wrappedFinalizer({});
|
|
5068
|
+
}
|
|
5069
|
+
|
|
5070
|
+
/**
|
|
5071
|
+
* Associe un compte MediaWiki à l'entité courante dans le contexte de l'instance.
|
|
5072
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
5073
|
+
*
|
|
5074
|
+
* @param username - Nom d'utilisateur MediaWiki à associer
|
|
5075
|
+
* @returns Résultat de la liaison avec les informations du compte MediaWiki trouvé
|
|
5076
|
+
* @throws {ApiError} Si `username` est absent ou invalide
|
|
5077
|
+
*
|
|
5078
|
+
* @example
|
|
5079
|
+
* const result = await user.linkMediaWikiAccount("John_Doe");
|
|
5080
|
+
* if (result.result) {
|
|
5081
|
+
* console.log(result.username); // nom d'utilisateur confirmé
|
|
5082
|
+
* } else {
|
|
5083
|
+
* console.error(result.error);
|
|
5084
|
+
* }
|
|
5085
|
+
*/
|
|
5086
|
+
async linkMediaWikiAccount(
|
|
5087
|
+
username: string,
|
|
5088
|
+
): Promise<{
|
|
5089
|
+
result: boolean;
|
|
5090
|
+
error?: string;
|
|
5091
|
+
username?: string;
|
|
5092
|
+
msg?: string;
|
|
5093
|
+
}> {
|
|
5094
|
+
if (!username || typeof username !== "string") {
|
|
5095
|
+
throw new ApiError("username est requis et doit être une chaîne de caractères.", 400);
|
|
5096
|
+
}
|
|
5097
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5098
|
+
(finalData: WithCostumContext<{ username: string }>) =>
|
|
5099
|
+
this.endpointApi.linkMediawikiAccount({
|
|
5100
|
+
...finalData,
|
|
5101
|
+
costumId: finalData.contextId,
|
|
5102
|
+
costumType: finalData.contextType,
|
|
5103
|
+
} as LinkMediawikiAccountData)
|
|
5104
|
+
);
|
|
5105
|
+
return wrappedFinalizer({ username });
|
|
5106
|
+
}
|
|
5107
|
+
|
|
5108
|
+
/**
|
|
5109
|
+
* Dissocie le compte MediaWiki de l'entité courante dans le contexte de l'instance.
|
|
5110
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
5111
|
+
*
|
|
5112
|
+
* @returns Résultat du délien du compte MediaWiki
|
|
5113
|
+
*
|
|
5114
|
+
* @example
|
|
5115
|
+
* const result = await user.unlinkMediaWikiAccount();
|
|
5116
|
+
* result.result; // true si succès
|
|
5117
|
+
*/
|
|
5118
|
+
async unlinkMediaWikiAccount(): Promise<{
|
|
5119
|
+
result: boolean;
|
|
5120
|
+
error?: string;
|
|
5121
|
+
msg?: string;
|
|
5122
|
+
}> {
|
|
5123
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5124
|
+
(finalData: WithCostumContext<Record<string, never>>) =>
|
|
5125
|
+
this.endpointApi.unlinkMediawikiAccount({
|
|
5126
|
+
...finalData,
|
|
5127
|
+
costumId: finalData.contextId,
|
|
5128
|
+
costumType: finalData.contextType,
|
|
5129
|
+
} as UnlinkMediawikiAccountData)
|
|
5130
|
+
);
|
|
5131
|
+
return wrappedFinalizer({});
|
|
5132
|
+
}
|
|
5133
|
+
|
|
5134
|
+
/**
|
|
5135
|
+
* Récupère les contributions MediaWiki d'un utilisateur dans le contexte de l'instance.
|
|
5136
|
+
* Injecte automatiquement le `costumSlug` via `_withCostumContext`.
|
|
5137
|
+
*
|
|
5138
|
+
* @param username - Nom d'utilisateur MediaWiki cible
|
|
5139
|
+
* @param limit - Nombre maximum de contributions à retourner (optionnel)
|
|
5140
|
+
* @returns Liste des contributions MediaWiki, ou `error` en cas d'échec
|
|
5141
|
+
* @throws {ApiError} Si `username` est absent ou invalide
|
|
5142
|
+
*
|
|
5143
|
+
* @example
|
|
5144
|
+
* const result = await user.getMediaWikiContributions("John_Doe", 10);
|
|
5145
|
+
* result.contribs; // objet contenant les contributions
|
|
5146
|
+
*/
|
|
5147
|
+
async getMediaWikiContributions(
|
|
5148
|
+
username: string,
|
|
5149
|
+
limit?: number,
|
|
5150
|
+
): Promise<{
|
|
5151
|
+
result: boolean;
|
|
5152
|
+
contribs?: Record<string, unknown>;
|
|
5153
|
+
}> {
|
|
5154
|
+
if (!username || typeof username !== "string") {
|
|
5155
|
+
throw new ApiError("username est requis et doit être une chaîne de caractères.", 400);
|
|
5156
|
+
}
|
|
5157
|
+
const wrappedFinalizer = this._withCostumContext(
|
|
5158
|
+
(finalData: WithCostumContext<{ username: string; limit?: number }>) =>
|
|
5159
|
+
this.endpointApi.getMediawikiContributions({
|
|
5160
|
+
...finalData,
|
|
5161
|
+
costumId: finalData.contextId,
|
|
5162
|
+
costumType: finalData.contextType,
|
|
5163
|
+
} as GetMediawikiContributionsData)
|
|
5164
|
+
);
|
|
5165
|
+
return wrappedFinalizer({ username, limit });
|
|
5166
|
+
}
|
|
5167
|
+
|
|
5168
|
+
/**
|
|
5169
|
+
* Wrapper pour l'endpoint COREMU_OPERATION : génère une proposition à partir
|
|
5170
|
+
* d'un formulaire et d'un projet. Cette méthode injecte les `const` du schéma
|
|
5171
|
+
* (`answer: "new"`, `action: "generateproposition"`) et délègue à l'EndpointApi.
|
|
5172
|
+
*
|
|
5173
|
+
* @param data.form - ID Mongo (24 hex) du formulaire
|
|
5174
|
+
* @param data.project - ID Mongo (24 hex) du projet
|
|
5175
|
+
* @returns Résultat brut de l'API (ou entité liée si `collection` est présent)
|
|
5176
|
+
* @throws {ApiError} Si les paramètres sont manquants ou invalides
|
|
5177
|
+
*/
|
|
5178
|
+
async coremuOperation(data: Pick<CoremuOperationData, "form" | "project">): Promise<any> {
|
|
5179
|
+
if (!data.form || typeof data.form !== "string") {
|
|
5180
|
+
throw new ApiError("form est requis et doit être une chaîne de caractères.", 400);
|
|
5181
|
+
}
|
|
5182
|
+
if (!data.project || typeof data.project !== "string") {
|
|
5183
|
+
throw new ApiError("project est requis et doit être une chaîne de caractères.", 400);
|
|
5184
|
+
}
|
|
5185
|
+
|
|
5186
|
+
const payload: CoremuOperationData = {
|
|
5187
|
+
...data,
|
|
5188
|
+
answer: "new",
|
|
5189
|
+
action: "generateproposition",
|
|
5190
|
+
};
|
|
5191
|
+
|
|
5192
|
+
const result = await this.endpointApi.coremuOperation(payload);
|
|
5193
|
+
return this._linkEntity(result?.collection, result) ?? result;
|
|
5194
|
+
}
|
|
5195
|
+
|
|
4861
5196
|
/**
|
|
4862
5197
|
* ───────────────────────────────
|
|
4863
5198
|
* Pagination restoration methods
|