• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GrottoCenter / grottocenter-api / 26314661498

22 May 2026 10:17PM UTC coverage: 87.042% (-0.01%) from 87.053%
26314661498

Pull #1604

github

ClemRz
fix(document): use TMassif and normalize IDs in populateJSON
Pull Request #1604: fix(document): use TMassif and normalize IDs in populateJSON

3256 of 3885 branches covered (83.81%)

Branch coverage included in aggregate %.

10 of 10 new or added lines in 1 file covered. (100.0%)

16 existing lines in 2 files now uncovered.

6578 of 7413 relevant lines covered (88.74%)

56.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.22
/api/controllers/v1/document/multiple-validate.js
1
const DocumentService = require('../../../services/DocumentService');
1✔
2
const FileService = require('../../../services/FileService');
1✔
3
const RightService = require('../../../services/RightService');
1✔
4
const NotificationService = require('../../../services/NotificationService');
1✔
5

6
async function markDocumentValidated(
7
  documentId,
8
  validationComment,
9
  validationAuthor
10
) {
11
  await TDocument.updateOne(documentId).set({
9✔
12
    isValidated: true,
13
    modifiedDocJson: null,
14
    dateValidation: new Date(),
15
    validationComment,
16
    validator: validationAuthor,
17
  });
18
}
19

20
async function validateAndUpdateDocument(
21
  document,
22
  validationComment,
23
  validationAuthor
24
) {
25
  const {
26
    reviewerId,
27
    documentData,
28
    descriptionData,
29
    modifiedFiles,
30
    deletedFiles,
31
    newFiles,
32
  } = document.modifiedDocJson;
1✔
33

34
  await sails.getDatastore().transaction(async (db) => {
1✔
35
    // Update associated data not handled by TDocument manually
36
    // Updated before the TDocument update so the last_change_document DB trigger will fetch the last updated name
37
    await TDescription.updateOne({ document: document.id })
1✔
38
      .set(descriptionData)
39
      .usingConnection(db);
40

41
    await TDocument.updateOne(document.id)
1✔
42
      .set({
43
        ...documentData,
44
        modifiedDocJson: null,
45
        dateReviewed: new Date(),
46
        reviewer: reviewerId,
47
        dateValidation: new Date(),
48
        isValidated: true,
49
        validationComment,
50
        validator: validationAuthor,
51
      })
52
      .usingConnection(db);
53

54
    const filePromises = [];
1✔
55
    // Files have already been created,
56
    // they just need to be linked to the document.
57
    if (newFiles) {
1!
UNCOV
58
      filePromises.push(
×
UNCOV
59
        ...newFiles.map((f) => TFile.updateOne(f.id).set({ isValidated: true }))
×
60
      );
61
    }
62
    if (modifiedFiles) {
1!
UNCOV
63
      filePromises.push(
×
UNCOV
64
        ...modifiedFiles.map((f) => FileService.document.update(f))
×
65
      );
66
    }
67

68
    if (deletedFiles) {
1!
UNCOV
69
      filePromises.push(
×
UNCOV
70
        ...deletedFiles.map((f) => FileService.document.delete(f))
×
71
      );
72
    }
73
    await Promise.all(filePromises);
1✔
74
  });
75
}
76

77
async function updateSearchAndNotify(req, documentId, userId) {
78
  const document = await DocumentService.getPopulatedDocument(documentId);
7✔
79
  await DocumentService.updateInSearch(document);
7✔
80

81
  await NotificationService.notifySubscribers(
7✔
82
    document,
83
    userId,
84
    NotificationService.NOTIFICATION_TYPES.VALIDATE,
85
    NotificationService.NOTIFICATION_ENTITIES.DOCUMENT
86
  );
87

88
  return document;
7✔
89
}
90

91
module.exports = async (req, res) => {
1✔
92
  const hasRight = RightService.hasGroup(
11✔
93
    req.token.groups,
94
    RightService.G.MODERATOR
95
  );
96
  if (!hasRight) {
11✔
97
    return res.forbidden(
1✔
98
      'You are not authorized to validate multiple documents.'
99
    );
100
  }
101

102
  const documentChanges = [];
10✔
103
  // Validate input
104
  for (const doc of req.param('documents') ?? []) {
10!
105
    // Whether or not the pending changes are accepted or not
106
    const isValidated = doc.isValidated
11!
107
      ? doc.isValidated.toLowerCase() !== 'false'
108
      : true;
109

110
    if (isValidated === false && !doc.validationComment) {
11✔
111
      return res.badRequest(
1✔
112
        `If the document with id ${doc.id} is refused, a comment must be provided.`
113
      );
114
    }
115

116
    documentChanges.push({
10✔
117
      id: doc.id,
118
      isValidated,
119
      validationComment: doc.validationComment,
120
    });
121
  }
122
  const documentIds = documentChanges.map((e) => e.id);
10✔
123
  const foundDocuments = await TDocument.find({ id: documentIds });
9✔
124

125
  // Sequential to preserve partial-success semantics per document
126
  for (const document of foundDocuments) {
9✔
127
    const change = documentChanges.find((d) => d.id === document.id);
12✔
128
    const isAModifiedDoc = !!document.modifiedDocJson;
10✔
129
    if (!change.isValidated) {
10✔
130
      // Validate it but do not update its fields (reject change)
131
      // eslint-disable-next-line no-await-in-loop
132
      await markDocumentValidated(
3✔
133
        document.id,
134
        change.validationComment,
135
        req.token.id
136
      );
137
      // eslint-disable-next-line no-await-in-loop
138
      const rejectedDoc = await DocumentService.getPopulatedDocument(
3✔
139
        document.id
140
      );
141
      // eslint-disable-next-line no-await-in-loop
142
      await NotificationService.notifyAuthor(
3✔
143
        rejectedDoc,
144
        req.token.id,
145
        NotificationService.NOTIFICATION_TYPES.REJECT,
146
        change.validationComment
147
      ).catch((err) =>
UNCOV
148
        sails.log.error(
×
149
          'Document multiple-validate notifyAuthor error',
150
          document,
151
          err
152
        )
153
      );
154
      continue; // eslint-disable-line no-continue
3✔
155
    }
156

157
    if (isAModifiedDoc) {
7✔
158
      // eslint-disable-next-line no-await-in-loop
159
      await validateAndUpdateDocument(
1✔
160
        document,
161
        change.validationComment,
162
        req.token.id
163
      );
164
    } else {
165
      // Likely a document creation
166
      // eslint-disable-next-line no-await-in-loop
167
      await markDocumentValidated(
6✔
168
        document.id,
169
        change.validationComment,
170
        req.token.id
171
      );
172
    }
173

174
    // eslint-disable-next-line no-await-in-loop
175
    const populatedDoc = await updateSearchAndNotify(
7✔
176
      req,
177
      document.id,
178
      req.token.id
179
    ).catch((err) => {
UNCOV
180
      sails.log.error(
×
181
        'Document multiple validate updateSearchAndNotify error',
182
        document,
183
        err
184
      );
UNCOV
185
      return null;
×
186
    });
187

188
    if (populatedDoc) {
7!
189
      // eslint-disable-next-line no-await-in-loop
190
      await NotificationService.notifyAuthor(
7✔
191
        populatedDoc,
192
        req.token.id,
193
        NotificationService.NOTIFICATION_TYPES.VALIDATE,
194
        change.validationComment
195
      ).catch((err) =>
UNCOV
196
        sails.log.error(
×
197
          'Document multiple-validate notifyAuthor error',
198
          document,
199
          err
200
        )
201
      );
202
    }
203
  }
204

205
  return res.ok();
9✔
206
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc