@programisto/edrm-exams 0.3.9 → 0.3.11
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.
|
@@ -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)
|
|
@@ -25,6 +25,23 @@ async function getJobName(targetJob) {
|
|
|
25
25
|
}
|
|
26
26
|
return 'Job inconnu';
|
|
27
27
|
}
|
|
28
|
+
/** Entité par défaut : les tests sans entityId lui sont rattachés (slug programisto/progamisto ou isDefault). */
|
|
29
|
+
function isDefaultEntity(req) {
|
|
30
|
+
if (!req?.entity)
|
|
31
|
+
return false;
|
|
32
|
+
const slug = req.entity.slug;
|
|
33
|
+
return req.entity.isDefault === true || slug === 'programisto' || slug === 'progamisto';
|
|
34
|
+
}
|
|
35
|
+
/** Vérifie qu'un test appartient à l'entité courante (pour GET/UPDATE/DELETE). Pour l'entité par défaut, un test sans entityId est accepté. */
|
|
36
|
+
function testBelongsToEntity(test, req) {
|
|
37
|
+
if (!req?.entity?._id)
|
|
38
|
+
return true;
|
|
39
|
+
const testEid = test?.entityId?.toString?.() ?? (test?.entityId != null ? String(test.entityId) : '');
|
|
40
|
+
const reqEid = req.entity._id?.toString?.() ?? String(req.entity._id);
|
|
41
|
+
if (isDefaultEntity(req))
|
|
42
|
+
return testEid === reqEid || testEid === '';
|
|
43
|
+
return testEid === reqEid;
|
|
44
|
+
}
|
|
28
45
|
// Fonction pour migrer automatiquement un test si nécessaire
|
|
29
46
|
async function migrateTestIfNeeded(test) {
|
|
30
47
|
if (typeof test.targetJob === 'string') {
|
|
@@ -404,7 +421,8 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
404
421
|
targetJob: targetJobId,
|
|
405
422
|
seniorityLevel,
|
|
406
423
|
state,
|
|
407
|
-
categories: processedCategories
|
|
424
|
+
categories: processedCategories,
|
|
425
|
+
...(req.entity?._id && { entityId: req.entity._id })
|
|
408
426
|
});
|
|
409
427
|
await newTest.save();
|
|
410
428
|
res.status(201).json({ message: 'test created with sucess', data: newTest });
|
|
@@ -451,6 +469,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
451
469
|
if (!test) {
|
|
452
470
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
453
471
|
}
|
|
472
|
+
if (!testBelongsToEntity(test, req)) {
|
|
473
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
474
|
+
}
|
|
454
475
|
if (title)
|
|
455
476
|
test.title = title;
|
|
456
477
|
if (description)
|
|
@@ -539,6 +560,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
539
560
|
if (!test) {
|
|
540
561
|
return res.status(404).json({ message: 'Test not found' });
|
|
541
562
|
}
|
|
563
|
+
if (!testBelongsToEntity(test, req)) {
|
|
564
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
565
|
+
}
|
|
542
566
|
for (let i = 0; i < test.questions.length; i++) {
|
|
543
567
|
await TestQuestion.findByIdAndDelete(test.questions[i].questionId);
|
|
544
568
|
}
|
|
@@ -579,6 +603,10 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
579
603
|
if (!test) {
|
|
580
604
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
581
605
|
}
|
|
606
|
+
// Vérifier que le test appartient à l'entité courante (multi-entités)
|
|
607
|
+
if (!testBelongsToEntity(test, req)) {
|
|
608
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
609
|
+
}
|
|
582
610
|
// Migration automatique si nécessaire
|
|
583
611
|
await migrateTestIfNeeded(test);
|
|
584
612
|
const questions = [];
|
|
@@ -666,6 +694,23 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
666
694
|
const sortOrder = req.query.sortOrder || 'desc';
|
|
667
695
|
// Construction de la requête de recherche
|
|
668
696
|
const query = {};
|
|
697
|
+
// Filtre par entité. Entité par défaut (programisto/progamisto ou isDefault) = tests sans entityId inclus.
|
|
698
|
+
if (req.entity?._id) {
|
|
699
|
+
if (isDefaultEntity(req)) {
|
|
700
|
+
query.$and = query.$and || [];
|
|
701
|
+
query.$and.push({
|
|
702
|
+
$or: [
|
|
703
|
+
{ entityId: req.entity._id },
|
|
704
|
+
{ entityId: req.entity._id.toString?.() ?? String(req.entity._id) },
|
|
705
|
+
{ entityId: { $exists: false } },
|
|
706
|
+
{ entityId: null }
|
|
707
|
+
]
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
query.entityId = req.entity._id;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
669
714
|
// Filtres
|
|
670
715
|
if (targetJob !== 'all') {
|
|
671
716
|
// Si on filtre par targetJob, on cherche d'abord le TestJob correspondant
|
|
@@ -784,9 +829,13 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
784
829
|
const category = await TestCategory.findOne({ name: categoryName });
|
|
785
830
|
if (!category)
|
|
786
831
|
return res.status(404).json({ message: 'Category not found' });
|
|
787
|
-
|
|
832
|
+
let test = await Test.findById(testId);
|
|
788
833
|
if (!test)
|
|
789
834
|
return res.status(404).json({ message: 'Test not found' });
|
|
835
|
+
if (!testBelongsToEntity(test, req)) {
|
|
836
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
837
|
+
}
|
|
838
|
+
test = await Test.findByIdAndUpdate(testId, { $pull: { categories: { categoryId: category._id } } }, { new: true });
|
|
790
839
|
res.status(200).json({ message: 'Category removed', test });
|
|
791
840
|
}
|
|
792
841
|
catch (err) {
|
|
@@ -839,6 +888,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
839
888
|
if (!test) {
|
|
840
889
|
return res.status(404).json({ message: 'Test not found' });
|
|
841
890
|
}
|
|
891
|
+
if (!testBelongsToEntity(test, req)) {
|
|
892
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
893
|
+
}
|
|
842
894
|
const categoryExists = test.categories.some(cat => cat.categoryId.equals(category._id));
|
|
843
895
|
if (categoryExists) {
|
|
844
896
|
return res.status(200).json({ message: 'Category already exists in the test' });
|
|
@@ -909,6 +961,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
909
961
|
if (!test) {
|
|
910
962
|
return res.status(404).json({ message: 'Test not found' });
|
|
911
963
|
}
|
|
964
|
+
if (!testBelongsToEntity(test, req)) {
|
|
965
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
966
|
+
}
|
|
912
967
|
const questions = [];
|
|
913
968
|
for (const questionId of test.questions) {
|
|
914
969
|
const question = await TestQuestion.findById(questionId);
|
|
@@ -959,6 +1014,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
959
1014
|
if (!test) {
|
|
960
1015
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
961
1016
|
}
|
|
1017
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1018
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1019
|
+
}
|
|
962
1020
|
// Supprimer la question du tableau questions en filtrant par questionId
|
|
963
1021
|
test.questions = test.questions.filter(q => q.questionId.toString() !== questionId);
|
|
964
1022
|
// Recalculer les ordres pour que ça se suive
|
|
@@ -995,6 +1053,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
995
1053
|
if (!test) {
|
|
996
1054
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
997
1055
|
}
|
|
1056
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1057
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1058
|
+
}
|
|
998
1059
|
for (const questionId of test.questions) {
|
|
999
1060
|
await TestQuestion.findByIdAndDelete(questionId);
|
|
1000
1061
|
}
|
|
@@ -1104,6 +1165,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1104
1165
|
if (!test) {
|
|
1105
1166
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1106
1167
|
}
|
|
1168
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1169
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1170
|
+
}
|
|
1107
1171
|
const question = new TestQuestion({
|
|
1108
1172
|
questionType,
|
|
1109
1173
|
instruction,
|
|
@@ -1162,6 +1226,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1162
1226
|
if (!test) {
|
|
1163
1227
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1164
1228
|
}
|
|
1229
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1230
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1231
|
+
}
|
|
1165
1232
|
const otherQuestionsIds = test.questions.map(question => question.questionId);
|
|
1166
1233
|
const otherQuestions = await TestQuestion.find({ _id: { $in: otherQuestionsIds } });
|
|
1167
1234
|
const jobName = await getJobName(test.targetJob);
|
|
@@ -1212,6 +1279,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1212
1279
|
if (!test) {
|
|
1213
1280
|
return res.status(404).json({ message: 'Test not found' });
|
|
1214
1281
|
}
|
|
1282
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1283
|
+
return res.status(404).json({ message: 'Test not found' });
|
|
1284
|
+
}
|
|
1215
1285
|
for (let i = test.questions.length - 1; i > 0; i--) {
|
|
1216
1286
|
const j = Math.floor(Math.random() * (i + 1));
|
|
1217
1287
|
[test.questions[i], test.questions[j]] = [test.questions[j], test.questions[i]];
|
|
@@ -1262,6 +1332,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1262
1332
|
if (!test) {
|
|
1263
1333
|
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1264
1334
|
}
|
|
1335
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1336
|
+
return res.status(404).json({ message: 'no test founded with this id' });
|
|
1337
|
+
}
|
|
1265
1338
|
test.invitationText = invitationText;
|
|
1266
1339
|
await test.save();
|
|
1267
1340
|
res.status(200).json({
|
|
@@ -1856,6 +1929,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1856
1929
|
if (!test) {
|
|
1857
1930
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1858
1931
|
}
|
|
1932
|
+
if (!testBelongsToEntity(test, req)) {
|
|
1933
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1934
|
+
}
|
|
1859
1935
|
let categoriesToUse = [];
|
|
1860
1936
|
if (category && category !== 'ALL') {
|
|
1861
1937
|
const categoryInfo = test.categories.find(cat => cat.categoryId.toString() === category);
|
|
@@ -1988,6 +2064,9 @@ class ExamsRouter extends EnduranceRouter {
|
|
|
1988
2064
|
if (!test) {
|
|
1989
2065
|
return res.status(404).json({ message: 'Test non trouvé' });
|
|
1990
2066
|
}
|
|
2067
|
+
if (!testBelongsToEntity(test, req)) {
|
|
2068
|
+
return res.status(404).json({ message: 'Test non trouvé' });
|
|
2069
|
+
}
|
|
1991
2070
|
// Construction de la requête
|
|
1992
2071
|
const query = { testId };
|
|
1993
2072
|
if (state !== 'all') {
|