Skip to content

Commit 06e756e

Browse files
authored
Merge pull request #1658 from CVEProject/dev
Feedback updates
2 parents 69b2859 + 2842f4d commit 06e756e

21 files changed

Lines changed: 433 additions & 275 deletions

api-docs/openapi.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,7 +5078,7 @@
50785078
}
50795079
}
50805080
},
5081-
"/review/org/{uuid}": {
5081+
"/review/{uuid}": {
50825082
"put": {
50835083
"tags": [
50845084
"Review Object"
@@ -5181,7 +5181,7 @@
51815181
}
51825182
}
51835183
},
5184-
"/review/org/{uuid}/approve": {
5184+
"/review/{uuid}/approve": {
51855185
"put": {
51865186
"tags": [
51875187
"Review Object"
@@ -5285,7 +5285,7 @@
52855285
}
52865286
}
52875287
},
5288-
"/review/org/{uuid}/reject": {
5288+
"/review/{uuid}/reject": {
52895289
"put": {
52905290
"tags": [
52915291
"Review Object"

package-lock.json

Lines changed: 86 additions & 86 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/controller/org.controller/org.controller.js

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ const validateUUID = require('uuid').validate
1717
*/
1818
async function getOrgs (req, res, next) {
1919
try {
20-
const session = await mongoose.startSession()
2120
const repo = req.ctx.repositories.getBaseOrgRepository()
2221
const CONSTANTS = getConstants()
23-
let returnValue
2422

2523
// temporary measure to allow tests to work after fixing #920
2624
// tests required changing the global limit to force pagination
@@ -32,11 +30,7 @@ async function getOrgs (req, res, next) {
3230
options.sort = { short_name: 'asc' }
3331
options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
3432

35-
try {
36-
returnValue = await repo.getAllOrgs({ ...options, session }, true)
37-
} finally {
38-
await session.endSession()
39-
}
33+
const returnValue = await repo.getAllOrgs({ ...options }, true)
4034

4135
logger.info({ uuid: req.ctx.uuid, message: 'The orgs were sent to the user.' })
4236
return res.status(200).json(returnValue)
@@ -58,7 +52,6 @@ async function getOrgs (req, res, next) {
5852
*/
5953
async function getOrg (req, res, next) {
6054
try {
61-
const session = await mongoose.startSession()
6255
const repo = req.ctx.repositories.getBaseOrgRepository()
6356
const requesterOrgShortName = req.ctx.org
6457
const identifier = req.ctx.params.identifier
@@ -67,26 +60,21 @@ async function getOrg (req, res, next) {
6760
let returnValue
6861

6962
try {
70-
session.startTransaction()
71-
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName, { session }, returnLegacyFormat)
63+
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName, {}, returnLegacyFormat)
7264
const requesterOrgIdentifier = identifierIsUUID ? requesterOrg.UUID : requesterOrgShortName
73-
const isSecretariat = await repo.isSecretariat(requesterOrg, { session }, returnLegacyFormat)
65+
const isSecretariat = await repo.isSecretariat(requesterOrg, {}, returnLegacyFormat)
7466

7567
if (requesterOrgIdentifier !== identifier && !isSecretariat) {
7668
logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
7769
return res.status(403).json(error.notSameOrgOrSecretariat())
7870
}
7971

80-
returnValue = await repo.getOrg(identifier, identifierIsUUID, { session }, returnLegacyFormat)
72+
returnValue = await repo.getOrg(identifier, identifierIsUUID, {}, returnLegacyFormat)
8173
} catch (error) {
82-
await session.abortTransaction()
8374
// Handle the specific error thrown by BaseOrgRepository.createOrg
8475
if (error.message && error.message.includes('Unknown Org type requested')) {
8576
return res.status(400).json({ message: error.message })
8677
}
87-
throw error
88-
} finally {
89-
await session.endSession()
9078
}
9179
if (!returnValue) { // an empty result can only happen if the requestor is the Secretariat
9280
logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization does not exist.' })
@@ -187,13 +175,13 @@ async function getUser (req, res, next) {
187175
return res.status(404).json(error.userDne(username))
188176
}
189177

190-
const rawResult = result
178+
const rawResult = result.toObject()
191179

192180
delete rawResult._id
193181
delete rawResult.__v
194182
delete rawResult.secret
195183

196-
logger.info({ uuid: req.ctx.uuid, message: username + ' was sent to the user.', user: result })
184+
logger.info({ uuid: req.ctx.uuid, message: username + ' was sent to the user.', user: rawResult })
197185
return res.status(200).json(rawResult)
198186
} catch (err) {
199187
next(err)
@@ -202,7 +190,7 @@ async function getUser (req, res, next) {
202190

203191
/**
204192
* Get details on ID quota for an org with the specified org shortname.
205-
* Called by GET /api/registry/org/{shortname}/id_quota, GET /api/org/{shortname}/id_quota
193+
* Called by GET /api/registry/org/{shortname}/hard_quota, GET /api/org/{shortname}/id_quota
206194
*
207195
* @param {Object} req - The request object
208196
* @param {Object} res - The response object
@@ -336,7 +324,7 @@ async function updateOrg (req, res, next) {
336324
const shortNameUrlParameter = req.ctx.params.shortname
337325
const orgRepository = req.ctx.repositories.getBaseOrgRepository()
338326

339-
const session = await mongoose.startSession()
327+
const session = await mongoose.startSession({ causalConsistency: false })
340328
let responseMessage
341329
// Get the query parameters as JSON
342330
// These are validated by the middleware in org/index.js
@@ -448,7 +436,7 @@ async function createUser (req, res, next) {
448436
return res.status(400).json({ message: 'Parameters were invalid', errors: result.errors })
449437
}
450438
} else {
451-
if (!body?.username || typeof body?.username !== 'string' || !body?.username.length > 0) {
439+
if (!body?.username || typeof body?.username !== 'string') {
452440
return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'username', msg: 'Parameter must be a non empty string' }] })
453441
}
454442
}

src/controller/registry-org.controller/registry-org.controller.js

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ const validateUUID = require('uuid').validate
2020
*/
2121
async function getAllOrgs (req, res, next) {
2222
try {
23-
const session = await mongoose.startSession()
2423
const repo = req.ctx.repositories.getBaseOrgRepository()
2524
const conversationRepo = req.ctx.repositories.getConversationRepository()
26-
const isSecretariat = await repo.isSecretariatByShortName(req.ctx.org, { session })
25+
const isSecretariat = await repo.isSecretariatByShortName(req.ctx.org)
2726
const CONSTANTS = getConstants()
2827
let returnValue
2928

@@ -38,14 +37,17 @@ async function getAllOrgs (req, res, next) {
3837
options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
3938

4039
try {
41-
returnValue = await repo.getAllOrgs({ ...options, session })
40+
returnValue = await repo.getAllOrgs({ ...options })
4241
// fetch conversations
4342
for (let i = 0; i < returnValue.organizations.length; i++) {
44-
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.organizations[i].UUID, isSecretariat, { session })
43+
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.organizations[i].UUID, isSecretariat)
4544
returnValue.organizations[i].conversation = conversation?.length ? conversation : undefined
4645
}
47-
} finally {
48-
await session.endSession()
46+
} catch (error) {
47+
// Handle the specific error thrown by BaseOrgRepository.createOrg
48+
if (error.message && error.message.includes('Unknown Org type requested')) {
49+
return res.status(400).json({ message: error.message })
50+
}
4951
}
5052

5153
logger.info({ uuid: req.ctx.uuid, message: 'The orgs were sent to the user.' })
@@ -69,7 +71,6 @@ async function getAllOrgs (req, res, next) {
6971
*/
7072
async function getOrg (req, res, next) {
7173
try {
72-
const session = await mongoose.startSession()
7374
const repo = req.ctx.repositories.getBaseOrgRepository()
7475
const conversationRepo = req.ctx.repositories.getConversationRepository()
7576
// User passed in parameter to filter for
@@ -79,32 +80,28 @@ async function getOrg (req, res, next) {
7980
let returnValue
8081

8182
try {
82-
session.startTransaction()
83-
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName, { session })
83+
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName)
8484
const requesterOrgIdentifier = identifierIsUUID ? requesterOrg.UUID : requesterOrgShortName
85-
const isSecretariat = await repo.isSecretariat(requesterOrg, { session })
85+
const isSecretariat = await repo.isSecretariat(requesterOrg)
8686

8787
if (requesterOrgIdentifier !== identifier && !isSecretariat) {
8888
logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
8989
return res.status(403).json(error.notSameOrgOrSecretariat())
9090
}
9191

92-
returnValue = await repo.getOrg(identifier, identifierIsUUID, { session })
92+
returnValue = await repo.getOrg(identifier, identifierIsUUID)
9393

9494
if (returnValue) {
9595
// fetch conversation
96-
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.UUID, isSecretariat, { session })
96+
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.UUID, isSecretariat)
9797
returnValue.conversation = conversation?.length ? _.map(conversation, c => _.omit(c, ['__v', '_id', 'UUID', 'previous_conversation_uuid', 'next_conversation_uuid', 'target_uuid', 'visibility'])) : undefined
9898
}
9999
} catch (error) {
100-
await session.abortTransaction()
101100
// Handle the specific error thrown by BaseOrgRepository.createOrg
102101
if (error.message && error.message.includes('Unknown Org type requested')) {
103102
return res.status(400).json({ message: error.message })
104103
}
105104
throw error
106-
} finally {
107-
await session.endSession()
108105
}
109106
if (!returnValue) { // an empty result can only happen if the requestor is the Secretariat
110107
logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization does not exist.' })
@@ -132,7 +129,7 @@ async function getOrg (req, res, next) {
132129
*/
133130
async function createOrg (req, res, next) {
134131
try {
135-
const session = await mongoose.startSession()
132+
const session = await mongoose.startSession({ causalConsistency: false })
136133
const repo = req.ctx.repositories.getBaseOrgRepository()
137134
const body = req.ctx.body
138135
const isSecretariat = await repo.isSecretariatByShortName(req.ctx.org, { session })
@@ -230,7 +227,7 @@ async function createOrg (req, res, next) {
230227
*/
231228
async function updateOrg (req, res, next) {
232229
try {
233-
const session = await mongoose.startSession()
230+
const session = await mongoose.startSession({ causalConsistency: false })
234231
const shortName = req.ctx.params.shortname
235232
const repo = req.ctx.repositories.getBaseOrgRepository()
236233
const userRepo = req.ctx.repositories.getBaseUserRepository()
@@ -316,15 +313,18 @@ async function updateOrg (req, res, next) {
316313
}
317314
}
318315

316+
// Update Org full will cause a write to the Conversations collection, to avoid a read-after-write issue, we need to get the previous conversation data first
317+
const previousConversation = await conversationRepo.getAllByTargetUUID(await repo.getOrgUUID(shortName, { session }), isSecretariat, { session }) || []
318+
319319
updatedOrg = await repo.updateOrgFull(shortName, req.ctx.body, { session }, false, requestingUser.UUID, isAdmin, isSecretariat)
320320
jointApprovalRequired = _.get(updatedOrg, 'joint_approval_required', false)
321321
_.unset(updatedOrg, 'joint_approval_required')
322-
323-
await session.commitTransaction()
324-
session.startTransaction()
325-
// Checking for existing Conversations
326-
const existingConversations = await conversationRepo.getAllByTargetUUID(updatedOrg.UUID, isSecretariat, { session }) || []
327-
updatedOrg.conversation = existingConversations.map(c => _.omit(c, ['__v', '_id', 'previous_conversation_uuid', 'next_conversation_uuid']))
322+
// append previous conversations to any conversations that are in the org already
323+
const currentConversations = Array.isArray(updatedOrg?.conversation) ? updatedOrg.conversation : []
324+
const prevConversations = Array.isArray(previousConversation) ? previousConversation : []
325+
if (updatedOrg) {
326+
updatedOrg.conversation = [...currentConversations, ...prevConversations].map(c => _.omit(c, ['__v', '_id', 'previous_conversation_uuid', 'next_conversation_uuid']))
327+
}
328328

329329
await session.commitTransaction()
330330
} catch (updateErr) {
@@ -385,7 +385,7 @@ async function updateOrg (req, res, next) {
385385
*/
386386
async function deleteOrg (req, res, next) {
387387
try {
388-
const session = await mongoose.startSession()
388+
const session = await mongoose.startSession({ causalConsistency: false })
389389
const repo = req.ctx.repositories.getBaseOrgRepository()
390390
const shortName = req.ctx.params.identifier
391391

@@ -498,7 +498,7 @@ async function getUsers (req, res, next) {
498498
* Called by POST /api/registryOrg/:shortname/user
499499
*/
500500
async function createUserByOrg (req, res, next) {
501-
const session = await mongoose.startSession()
501+
const session = await mongoose.startSession({ causalConsistency: false })
502502
try {
503503
const body = req.ctx.body
504504
const userRepo = req.ctx.repositories.getBaseUserRepository()

src/controller/registry-user.controller/registry-user.controller.js

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ const _ = require('lodash')
2121
async function getAllUsers (req, res, next) {
2222
try {
2323
const CONSTANTS = getConstants()
24-
const session = await mongoose.startSession()
2524
const repo = req.ctx.repositories.getBaseUserRepository()
26-
let returnValue
2725

2826
// temporary measure to allow tests to work after fixing #920
2927
// tests required changing the global limit to force pagination
@@ -35,36 +33,32 @@ async function getAllUsers (req, res, next) {
3533
options.sort = { short_name: 'asc' }
3634
options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
3735

38-
try {
39-
returnValue = await repo.getAllUsers(options)
40-
// Hydrate roles
41-
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
42-
const distinctOrgUUIDs = [...new Set(returnValue.users.map(u => u.org_UUID))]
43-
44-
// Fetch all relevant orgs in one go (or in parallel) if possible, but map is easy for now
45-
// Since we don't have a "getManyOrgsByUUID", we might need to do it one by one or improve repository
46-
// For now, let's iterate and fetch. It's not optimal but safe given repo limitations.
47-
// Optimization: We can build a map of orgUUID -> orgObject
48-
const orgMap = {}
49-
for (const uuid of distinctOrgUUIDs) {
50-
// We need the org content to get admins
51-
const org = await orgRepo.findOneByUUID(uuid)
52-
if (org) {
53-
orgMap[uuid] = org
54-
}
36+
const returnValue = await repo.getAllUsers(options)
37+
// Hydrate roles
38+
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
39+
const distinctOrgUUIDs = [...new Set(returnValue.users.map(u => u.org_UUID))]
40+
41+
// Fetch all relevant orgs in one go (or in parallel) if possible, but map is easy for now
42+
// Since we don't have a "getManyOrgsByUUID", we might need to do it one by one or improve repository
43+
// For now, let's iterate and fetch. It's not optimal but safe given repo limitations.
44+
// Optimization: We can build a map of orgUUID -> orgObject
45+
const orgMap = {}
46+
for (const uuid of distinctOrgUUIDs) {
47+
// We need the org content to get admins
48+
const org = await orgRepo.findOneByUUID(uuid)
49+
if (org) {
50+
orgMap[uuid] = org
5551
}
56-
57-
returnValue.users.forEach(user => {
58-
const org = orgMap[user.org_UUID]
59-
if (org && org.admins && org.admins.includes(user.UUID)) {
60-
user.role = 'ADMIN'
61-
}
62-
// If not admin, leave as is (undefined or empty or whatever it was)
63-
})
64-
} finally {
65-
await session.endSession()
6652
}
6753

54+
returnValue.users.forEach(user => {
55+
const org = orgMap[user.org_UUID]
56+
if (org && org.admins && org.admins.includes(user.UUID)) {
57+
user.role = 'ADMIN'
58+
}
59+
// If not admin, leave as is (undefined or empty or whatever it was)
60+
})
61+
6862
logger.info({ uuid: req.ctx.uuid, message: 'The user information was sent to the secretariat user.' })
6963
return res.status(200).json(returnValue)
7064
} catch (err) {
@@ -143,7 +137,7 @@ async function getUser (req, res, next) {
143137
}
144138

145139
async function createUser (req, res, next) {
146-
const session = await mongoose.startSession()
140+
const session = await mongoose.startSession({ causalConsistency: false })
147141
try {
148142
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
149143
const userRepo = req.ctx.repositories.getBaseUserRepository()

src/controller/review-object.controller/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ router.get('/review/org/:identifier/reviews',
324324
)
325325

326326
// Update a review object
327-
router.put('/review/org/:uuid',
327+
router.put('/review/:uuid',
328328
/*
329329
#swagger.tags = ['Review Object']
330330
#swagger.operationId = 'updateReviewObjectByReviewUUID'
@@ -407,7 +407,7 @@ router.put('/review/org/:uuid',
407407
)
408408

409409
// Approve a review object
410-
router.put('/review/org/:uuid/approve',
410+
router.put('/review/:uuid/approve',
411411
/*
412412
#swagger.tags = ['Review Object']
413413
#swagger.operationId = 'approveReviewObject'
@@ -491,7 +491,7 @@ router.put('/review/org/:uuid/approve',
491491
)
492492

493493
// Reject a review object
494-
router.put('/review/org/:uuid/reject',
494+
router.put('/review/:uuid/reject',
495495
/*
496496
#swagger.tags = ['Review Object']
497497
#swagger.operationId = 'rejectReviewObject'

0 commit comments

Comments
 (0)