@programisto/edrm-exams 0.3.8 → 0.3.10
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/README.md +1 -1
- package/dist/modules/edrm-exams/models/test.model.d.ts +3 -0
- package/dist/modules/edrm-exams/models/test.model.js +7 -0
- package/dist/modules/edrm-exams/routes/company.router.js +135 -6
- package/dist/modules/edrm-exams/routes/exams-candidate.router.js +226 -7
- package/dist/modules/edrm-exams/routes/exams.router.js +1072 -41
- package/dist/modules/edrm-exams/routes/result.router.js +152 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Types } from 'mongoose';
|
|
1
2
|
import { EnduranceSchema } from '@programisto/endurance';
|
|
2
3
|
import Company from './company.model.js';
|
|
3
4
|
import TestQuestion from './test-question.model.js';
|
|
@@ -32,6 +33,8 @@ declare class Test extends EnduranceSchema {
|
|
|
32
33
|
title: string;
|
|
33
34
|
description: string;
|
|
34
35
|
companyId: typeof Company;
|
|
36
|
+
/** Identifiant de l'entité (portail multi-entités). Optionnel pour rétrocompatibilité. */
|
|
37
|
+
entityId?: Types.ObjectId;
|
|
35
38
|
userId: typeof User;
|
|
36
39
|
questions: TestQuestions[];
|
|
37
40
|
state: TestState;
|
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
import { Types } from 'mongoose';
|
|
10
11
|
import { EnduranceSchema, EnduranceModelType } from '@programisto/endurance';
|
|
11
12
|
import Company from './company.model.js';
|
|
12
13
|
import TestJob from './test-job.model.js';
|
|
@@ -44,6 +45,8 @@ let Test = class Test extends EnduranceSchema {
|
|
|
44
45
|
title;
|
|
45
46
|
description;
|
|
46
47
|
companyId;
|
|
48
|
+
/** Identifiant de l'entité (portail multi-entités). Optionnel pour rétrocompatibilité. */
|
|
49
|
+
entityId;
|
|
47
50
|
userId;
|
|
48
51
|
questions;
|
|
49
52
|
state;
|
|
@@ -90,6 +93,10 @@ __decorate([
|
|
|
90
93
|
EnduranceModelType.prop({ ref: () => Company, required: false }),
|
|
91
94
|
__metadata("design:type", Object)
|
|
92
95
|
], Test.prototype, "companyId", void 0);
|
|
96
|
+
__decorate([
|
|
97
|
+
EnduranceModelType.prop({ required: false }),
|
|
98
|
+
__metadata("design:type", Types.ObjectId)
|
|
99
|
+
], Test.prototype, "entityId", void 0);
|
|
93
100
|
__decorate([
|
|
94
101
|
EnduranceModelType.prop({ ref: () => User, required: false }),
|
|
95
102
|
__metadata("design:type", Object)
|
|
@@ -10,7 +10,27 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
10
10
|
requireAuth: false,
|
|
11
11
|
permissions: []
|
|
12
12
|
};
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @swagger
|
|
15
|
+
* /companies/{id}:
|
|
16
|
+
* get:
|
|
17
|
+
* summary: Détail d'une entreprise
|
|
18
|
+
* description: Récupère une entreprise par son identifiant.
|
|
19
|
+
* tags: [Entreprises]
|
|
20
|
+
* parameters:
|
|
21
|
+
* - in: path
|
|
22
|
+
* name: id
|
|
23
|
+
* required: true
|
|
24
|
+
* schema:
|
|
25
|
+
* type: string
|
|
26
|
+
* responses:
|
|
27
|
+
* 200:
|
|
28
|
+
* description: Entreprise trouvée
|
|
29
|
+
* 404:
|
|
30
|
+
* description: Entreprise non trouvée
|
|
31
|
+
* 500:
|
|
32
|
+
* description: Erreur interne
|
|
33
|
+
*/
|
|
14
34
|
this.get('/:id', authenticatedOptions, async (req, res) => {
|
|
15
35
|
const { id } = req.params;
|
|
16
36
|
try {
|
|
@@ -25,7 +45,33 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
25
45
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
26
46
|
}
|
|
27
47
|
});
|
|
28
|
-
|
|
48
|
+
/**
|
|
49
|
+
* @swagger
|
|
50
|
+
* /companies/create:
|
|
51
|
+
* post:
|
|
52
|
+
* summary: Créer une entreprise
|
|
53
|
+
* description: Crée une nouvelle entreprise avec nom et logo.
|
|
54
|
+
* tags: [Entreprises]
|
|
55
|
+
* requestBody:
|
|
56
|
+
* required: true
|
|
57
|
+
* content:
|
|
58
|
+
* application/json:
|
|
59
|
+
* schema:
|
|
60
|
+
* type: object
|
|
61
|
+
* required: [name, logo]
|
|
62
|
+
* properties:
|
|
63
|
+
* name:
|
|
64
|
+
* type: string
|
|
65
|
+
* logo:
|
|
66
|
+
* type: string
|
|
67
|
+
* responses:
|
|
68
|
+
* 201:
|
|
69
|
+
* description: Entreprise créée
|
|
70
|
+
* 400:
|
|
71
|
+
* description: Paramètres manquants
|
|
72
|
+
* 500:
|
|
73
|
+
* description: Erreur interne
|
|
74
|
+
*/
|
|
29
75
|
this.post('/create', authenticatedOptions, async (req, res) => {
|
|
30
76
|
const { name, logo } = req.body;
|
|
31
77
|
if (!name || !logo) {
|
|
@@ -41,7 +87,38 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
41
87
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
42
88
|
}
|
|
43
89
|
});
|
|
44
|
-
|
|
90
|
+
/**
|
|
91
|
+
* @swagger
|
|
92
|
+
* /companies/{id}:
|
|
93
|
+
* put:
|
|
94
|
+
* summary: Mettre à jour une entreprise
|
|
95
|
+
* description: Met à jour le nom ou le logo d'une entreprise.
|
|
96
|
+
* tags: [Entreprises]
|
|
97
|
+
* parameters:
|
|
98
|
+
* - in: path
|
|
99
|
+
* name: id
|
|
100
|
+
* required: true
|
|
101
|
+
* schema:
|
|
102
|
+
* type: string
|
|
103
|
+
* requestBody:
|
|
104
|
+
* required: true
|
|
105
|
+
* content:
|
|
106
|
+
* application/json:
|
|
107
|
+
* schema:
|
|
108
|
+
* type: object
|
|
109
|
+
* properties:
|
|
110
|
+
* name:
|
|
111
|
+
* type: string
|
|
112
|
+
* logo:
|
|
113
|
+
* type: string
|
|
114
|
+
* responses:
|
|
115
|
+
* 200:
|
|
116
|
+
* description: Entreprise mise à jour
|
|
117
|
+
* 404:
|
|
118
|
+
* description: Entreprise non trouvée
|
|
119
|
+
* 500:
|
|
120
|
+
* description: Erreur interne
|
|
121
|
+
*/
|
|
45
122
|
this.put('/:id', authenticatedOptions, async (req, res) => {
|
|
46
123
|
const { id } = req.params;
|
|
47
124
|
const { name, logo } = req.body;
|
|
@@ -63,7 +140,27 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
63
140
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
64
141
|
}
|
|
65
142
|
});
|
|
66
|
-
|
|
143
|
+
/**
|
|
144
|
+
* @swagger
|
|
145
|
+
* /companies/{id}:
|
|
146
|
+
* delete:
|
|
147
|
+
* summary: Supprimer une entreprise
|
|
148
|
+
* description: Supprime une entreprise par son ID.
|
|
149
|
+
* tags: [Entreprises]
|
|
150
|
+
* parameters:
|
|
151
|
+
* - in: path
|
|
152
|
+
* name: id
|
|
153
|
+
* required: true
|
|
154
|
+
* schema:
|
|
155
|
+
* type: string
|
|
156
|
+
* responses:
|
|
157
|
+
* 200:
|
|
158
|
+
* description: Entreprise supprimée
|
|
159
|
+
* 404:
|
|
160
|
+
* description: Entreprise non trouvée
|
|
161
|
+
* 500:
|
|
162
|
+
* description: Erreur interne
|
|
163
|
+
*/
|
|
67
164
|
this.delete('/:id', authenticatedOptions, async (req, res) => {
|
|
68
165
|
const { id } = req.params;
|
|
69
166
|
try {
|
|
@@ -78,7 +175,27 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
78
175
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
79
176
|
}
|
|
80
177
|
});
|
|
81
|
-
|
|
178
|
+
/**
|
|
179
|
+
* @swagger
|
|
180
|
+
* /companies/numberOfUser/{id}:
|
|
181
|
+
* get:
|
|
182
|
+
* summary: Nombre d'utilisateurs d'une entreprise
|
|
183
|
+
* description: Retourne le nombre d'utilisateurs associés à l'entreprise.
|
|
184
|
+
* tags: [Entreprises]
|
|
185
|
+
* parameters:
|
|
186
|
+
* - in: path
|
|
187
|
+
* name: id
|
|
188
|
+
* required: true
|
|
189
|
+
* schema:
|
|
190
|
+
* type: string
|
|
191
|
+
* responses:
|
|
192
|
+
* 200:
|
|
193
|
+
* description: Nombre d'utilisateurs retourné
|
|
194
|
+
* 404:
|
|
195
|
+
* description: Entreprise non trouvée
|
|
196
|
+
* 500:
|
|
197
|
+
* description: Erreur interne
|
|
198
|
+
*/
|
|
82
199
|
this.get('/numberOfUser/:id', authenticatedOptions, async (req, res) => {
|
|
83
200
|
const { id } = req.params;
|
|
84
201
|
try {
|
|
@@ -91,7 +208,19 @@ class CompanyRouter extends EnduranceRouter {
|
|
|
91
208
|
res.status(500).json({ message: 'Erreur interne du serveur' });
|
|
92
209
|
}
|
|
93
210
|
});
|
|
94
|
-
|
|
211
|
+
/**
|
|
212
|
+
* @swagger
|
|
213
|
+
* /companies:
|
|
214
|
+
* get:
|
|
215
|
+
* summary: Lister les entreprises
|
|
216
|
+
* description: Retourne l'ensemble des entreprises.
|
|
217
|
+
* tags: [Entreprises]
|
|
218
|
+
* responses:
|
|
219
|
+
* 200:
|
|
220
|
+
* description: Liste des entreprises
|
|
221
|
+
* 500:
|
|
222
|
+
* description: Erreur interne
|
|
223
|
+
*/
|
|
95
224
|
this.get('/', authenticatedOptions, async (req, res) => {
|
|
96
225
|
try {
|
|
97
226
|
const companies = await Company.find();
|
|
@@ -32,7 +32,51 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
32
32
|
requireAuth: false,
|
|
33
33
|
permissions: []
|
|
34
34
|
};
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @swagger
|
|
37
|
+
* /candidates:
|
|
38
|
+
* post:
|
|
39
|
+
* summary: Créer un candidat
|
|
40
|
+
* description: Crée un contact et un candidat ou réutilise un contact existant selon l'email.
|
|
41
|
+
* tags: [Candidats]
|
|
42
|
+
* requestBody:
|
|
43
|
+
* required: true
|
|
44
|
+
* content:
|
|
45
|
+
* application/json:
|
|
46
|
+
* schema:
|
|
47
|
+
* type: object
|
|
48
|
+
* required: [firstname, lastname, email, city, skills]
|
|
49
|
+
* properties:
|
|
50
|
+
* firstname:
|
|
51
|
+
* type: string
|
|
52
|
+
* lastname:
|
|
53
|
+
* type: string
|
|
54
|
+
* email:
|
|
55
|
+
* type: string
|
|
56
|
+
* phone:
|
|
57
|
+
* type: string
|
|
58
|
+
* linkedin:
|
|
59
|
+
* type: string
|
|
60
|
+
* city:
|
|
61
|
+
* type: string
|
|
62
|
+
* experienceLevel:
|
|
63
|
+
* type: string
|
|
64
|
+
* yearsOfExperience:
|
|
65
|
+
* type: number
|
|
66
|
+
* skills:
|
|
67
|
+
* type: array
|
|
68
|
+
* items:
|
|
69
|
+
* type: string
|
|
70
|
+
* responses:
|
|
71
|
+
* 201:
|
|
72
|
+
* description: Candidat créé
|
|
73
|
+
* 200:
|
|
74
|
+
* description: Candidat existant retourné
|
|
75
|
+
* 400:
|
|
76
|
+
* description: Paramètres manquants
|
|
77
|
+
* 500:
|
|
78
|
+
* description: Erreur interne
|
|
79
|
+
*/
|
|
36
80
|
this.post('/', authenticatedOptions, async (req, res) => {
|
|
37
81
|
const { firstname, lastname, email, phone, linkedin, city, experienceLevel, yearsOfExperience, skills } = req.body;
|
|
38
82
|
console.log(req.body);
|
|
@@ -113,7 +157,45 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
113
157
|
res.status(500).json({ message: 'Internal server error' });
|
|
114
158
|
}
|
|
115
159
|
});
|
|
116
|
-
|
|
160
|
+
/**
|
|
161
|
+
* @swagger
|
|
162
|
+
* /candidates:
|
|
163
|
+
* get:
|
|
164
|
+
* summary: Lister les candidats
|
|
165
|
+
* description: Retourne les candidats avec contact, pagination, recherche et tri.
|
|
166
|
+
* tags: [Candidats]
|
|
167
|
+
* parameters:
|
|
168
|
+
* - in: query
|
|
169
|
+
* name: page
|
|
170
|
+
* schema:
|
|
171
|
+
* type: integer
|
|
172
|
+
* default: 1
|
|
173
|
+
* - in: query
|
|
174
|
+
* name: limit
|
|
175
|
+
* schema:
|
|
176
|
+
* type: integer
|
|
177
|
+
* default: 10
|
|
178
|
+
* - in: query
|
|
179
|
+
* name: search
|
|
180
|
+
* schema:
|
|
181
|
+
* type: string
|
|
182
|
+
* description: Recherche sur prénom, nom, email
|
|
183
|
+
* - in: query
|
|
184
|
+
* name: sortBy
|
|
185
|
+
* schema:
|
|
186
|
+
* type: string
|
|
187
|
+
* default: lastname
|
|
188
|
+
* - in: query
|
|
189
|
+
* name: sortOrder
|
|
190
|
+
* schema:
|
|
191
|
+
* type: string
|
|
192
|
+
* default: asc
|
|
193
|
+
* responses:
|
|
194
|
+
* 200:
|
|
195
|
+
* description: Liste paginée des candidats
|
|
196
|
+
* 500:
|
|
197
|
+
* description: Erreur interne
|
|
198
|
+
*/
|
|
117
199
|
this.get('/', authenticatedOptions, async (req, res) => {
|
|
118
200
|
try {
|
|
119
201
|
const page = parseInt(req.query.page) || 1;
|
|
@@ -207,7 +289,27 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
207
289
|
res.status(500).json({ message: 'Internal server error' });
|
|
208
290
|
}
|
|
209
291
|
});
|
|
210
|
-
|
|
292
|
+
/**
|
|
293
|
+
* @swagger
|
|
294
|
+
* /candidates/{id}:
|
|
295
|
+
* get:
|
|
296
|
+
* summary: Détail d'un candidat
|
|
297
|
+
* description: Récupère un candidat et son contact par ID.
|
|
298
|
+
* tags: [Candidats]
|
|
299
|
+
* parameters:
|
|
300
|
+
* - in: path
|
|
301
|
+
* name: id
|
|
302
|
+
* required: true
|
|
303
|
+
* schema:
|
|
304
|
+
* type: string
|
|
305
|
+
* responses:
|
|
306
|
+
* 200:
|
|
307
|
+
* description: Candidat trouvé
|
|
308
|
+
* 404:
|
|
309
|
+
* description: Candidat non trouvé
|
|
310
|
+
* 500:
|
|
311
|
+
* description: Erreur interne
|
|
312
|
+
*/
|
|
211
313
|
this.get('/:id', authenticatedOptions, async (req, res) => {
|
|
212
314
|
const { id } = req.params;
|
|
213
315
|
try {
|
|
@@ -233,7 +335,27 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
233
335
|
res.status(500).json({ message: 'Internal server error' });
|
|
234
336
|
}
|
|
235
337
|
});
|
|
236
|
-
|
|
338
|
+
/**
|
|
339
|
+
* @swagger
|
|
340
|
+
* /candidates/email/{email}:
|
|
341
|
+
* get:
|
|
342
|
+
* summary: Détail d'un candidat par email
|
|
343
|
+
* description: Cherche le contact par email puis retourne le candidat associé.
|
|
344
|
+
* tags: [Candidats]
|
|
345
|
+
* parameters:
|
|
346
|
+
* - in: path
|
|
347
|
+
* name: email
|
|
348
|
+
* required: true
|
|
349
|
+
* schema:
|
|
350
|
+
* type: string
|
|
351
|
+
* responses:
|
|
352
|
+
* 200:
|
|
353
|
+
* description: Candidat trouvé
|
|
354
|
+
* 404:
|
|
355
|
+
* description: Contact ou candidat non trouvé
|
|
356
|
+
* 500:
|
|
357
|
+
* description: Erreur interne
|
|
358
|
+
*/
|
|
237
359
|
this.get('/email/:email', authenticatedOptions, async (req, res) => {
|
|
238
360
|
try {
|
|
239
361
|
const email = req.params.email;
|
|
@@ -257,7 +379,33 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
257
379
|
res.status(500).send('Erreur interne du serveur');
|
|
258
380
|
}
|
|
259
381
|
});
|
|
260
|
-
|
|
382
|
+
/**
|
|
383
|
+
* @swagger
|
|
384
|
+
* /candidates/magic-link:
|
|
385
|
+
* post:
|
|
386
|
+
* summary: Générer un lien magique
|
|
387
|
+
* description: Génère un token d'accès court pour un candidat et envoie l'email.
|
|
388
|
+
* tags: [Candidats]
|
|
389
|
+
* requestBody:
|
|
390
|
+
* required: true
|
|
391
|
+
* content:
|
|
392
|
+
* application/json:
|
|
393
|
+
* schema:
|
|
394
|
+
* type: object
|
|
395
|
+
* required: [email]
|
|
396
|
+
* properties:
|
|
397
|
+
* email:
|
|
398
|
+
* type: string
|
|
399
|
+
* responses:
|
|
400
|
+
* 200:
|
|
401
|
+
* description: Lien envoyé
|
|
402
|
+
* 404:
|
|
403
|
+
* description: Contact ou candidat non trouvé
|
|
404
|
+
* 400:
|
|
405
|
+
* description: Email manquant
|
|
406
|
+
* 500:
|
|
407
|
+
* description: Erreur interne
|
|
408
|
+
*/
|
|
261
409
|
this.post('/magic-link', { requireAuth: false }, async (req, res) => {
|
|
262
410
|
try {
|
|
263
411
|
const { email } = req.body;
|
|
@@ -303,7 +451,33 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
303
451
|
res.status(500).send('Erreur interne du serveur');
|
|
304
452
|
}
|
|
305
453
|
});
|
|
306
|
-
|
|
454
|
+
/**
|
|
455
|
+
* @swagger
|
|
456
|
+
* /candidates/verify-magic-link:
|
|
457
|
+
* post:
|
|
458
|
+
* summary: Vérifier un lien magique
|
|
459
|
+
* description: Valide le token magique, génère un authToken 24h et retourne le candidat.
|
|
460
|
+
* tags: [Candidats]
|
|
461
|
+
* requestBody:
|
|
462
|
+
* required: true
|
|
463
|
+
* content:
|
|
464
|
+
* application/json:
|
|
465
|
+
* schema:
|
|
466
|
+
* type: object
|
|
467
|
+
* required: [token]
|
|
468
|
+
* properties:
|
|
469
|
+
* token:
|
|
470
|
+
* type: string
|
|
471
|
+
* responses:
|
|
472
|
+
* 200:
|
|
473
|
+
* description: Authentification réussie
|
|
474
|
+
* 400:
|
|
475
|
+
* description: Token manquant
|
|
476
|
+
* 401:
|
|
477
|
+
* description: Token invalide ou expiré
|
|
478
|
+
* 500:
|
|
479
|
+
* description: Erreur interne
|
|
480
|
+
*/
|
|
307
481
|
this.post('/verify-magic-link', { requireAuth: false }, async (req, res) => {
|
|
308
482
|
try {
|
|
309
483
|
const { token } = req.body;
|
|
@@ -357,7 +531,52 @@ class CandidateRouter extends EnduranceRouter {
|
|
|
357
531
|
res.status(500).send('Erreur interne du serveur');
|
|
358
532
|
}
|
|
359
533
|
});
|
|
360
|
-
|
|
534
|
+
/**
|
|
535
|
+
* @swagger
|
|
536
|
+
* /candidates/results/{candidateId}:
|
|
537
|
+
* get:
|
|
538
|
+
* summary: Lister les résultats d'un candidat
|
|
539
|
+
* description: Liste paginée des résultats d'un candidat avec filtrage état et tri.
|
|
540
|
+
* tags: [Candidats]
|
|
541
|
+
* parameters:
|
|
542
|
+
* - in: path
|
|
543
|
+
* name: candidateId
|
|
544
|
+
* required: true
|
|
545
|
+
* schema:
|
|
546
|
+
* type: string
|
|
547
|
+
* - in: query
|
|
548
|
+
* name: page
|
|
549
|
+
* schema:
|
|
550
|
+
* type: integer
|
|
551
|
+
* default: 1
|
|
552
|
+
* - in: query
|
|
553
|
+
* name: limit
|
|
554
|
+
* schema:
|
|
555
|
+
* type: integer
|
|
556
|
+
* default: 10
|
|
557
|
+
* - in: query
|
|
558
|
+
* name: state
|
|
559
|
+
* schema:
|
|
560
|
+
* type: string
|
|
561
|
+
* default: all
|
|
562
|
+
* - in: query
|
|
563
|
+
* name: sortBy
|
|
564
|
+
* schema:
|
|
565
|
+
* type: string
|
|
566
|
+
* default: invitationDate
|
|
567
|
+
* - in: query
|
|
568
|
+
* name: sortOrder
|
|
569
|
+
* schema:
|
|
570
|
+
* type: string
|
|
571
|
+
* default: desc
|
|
572
|
+
* responses:
|
|
573
|
+
* 200:
|
|
574
|
+
* description: Résultats paginés
|
|
575
|
+
* 404:
|
|
576
|
+
* description: Candidat non trouvé
|
|
577
|
+
* 500:
|
|
578
|
+
* description: Erreur interne
|
|
579
|
+
*/
|
|
361
580
|
this.get('/results/:candidateId', authenticatedOptions, async (req, res) => {
|
|
362
581
|
try {
|
|
363
582
|
const { candidateId } = req.params;
|