Skip to content

Commit 1272c83

Browse files
authored
Merge branch 'dev' into dependabot/npm_and_yarn/minimatch-3.1.5
2 parents 692ebf7 + 3e63556 commit 1272c83

8 files changed

Lines changed: 82 additions & 96 deletions

File tree

package-lock.json

Lines changed: 12 additions & 12 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: 5 additions & 17 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.' })
@@ -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

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

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ async function getAllOrgs (req, res, next) {
7171
*/
7272
async function getOrg (req, res, next) {
7373
try {
74-
const session = await mongoose.startSession()
7574
const repo = req.ctx.repositories.getBaseOrgRepository()
7675
const conversationRepo = req.ctx.repositories.getConversationRepository()
7776
// User passed in parameter to filter for
@@ -81,32 +80,28 @@ async function getOrg (req, res, next) {
8180
let returnValue
8281

8382
try {
84-
session.startTransaction()
85-
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName, { session })
83+
const requesterOrg = await repo.findOneByShortName(requesterOrgShortName)
8684
const requesterOrgIdentifier = identifierIsUUID ? requesterOrg.UUID : requesterOrgShortName
87-
const isSecretariat = await repo.isSecretariat(requesterOrg, { session })
85+
const isSecretariat = await repo.isSecretariat(requesterOrg)
8886

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

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

9694
if (returnValue) {
9795
// fetch conversation
98-
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.UUID, isSecretariat, { session })
96+
const conversation = await conversationRepo.getAllByTargetUUID(returnValue.UUID, isSecretariat)
9997
returnValue.conversation = conversation?.length ? _.map(conversation, c => _.omit(c, ['__v', '_id', 'UUID', 'previous_conversation_uuid', 'next_conversation_uuid', 'target_uuid', 'visibility'])) : undefined
10098
}
10199
} catch (error) {
102-
await session.abortTransaction()
103100
// Handle the specific error thrown by BaseOrgRepository.createOrg
104101
if (error.message && error.message.includes('Unknown Org type requested')) {
105102
return res.status(400).json({ message: error.message })
106103
}
107104
throw error
108-
} finally {
109-
await session.endSession()
110105
}
111106
if (!returnValue) { // an empty result can only happen if the requestor is the Secretariat
112107
logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization does not exist.' })
@@ -134,7 +129,7 @@ async function getOrg (req, res, next) {
134129
*/
135130
async function createOrg (req, res, next) {
136131
try {
137-
const session = await mongoose.startSession()
132+
const session = await mongoose.startSession({ causalConsistency: false })
138133
const repo = req.ctx.repositories.getBaseOrgRepository()
139134
const body = req.ctx.body
140135
const isSecretariat = await repo.isSecretariatByShortName(req.ctx.org, { session })
@@ -232,7 +227,7 @@ async function createOrg (req, res, next) {
232227
*/
233228
async function updateOrg (req, res, next) {
234229
try {
235-
const session = await mongoose.startSession()
230+
const session = await mongoose.startSession({ causalConsistency: false })
236231
const shortName = req.ctx.params.shortname
237232
const repo = req.ctx.repositories.getBaseOrgRepository()
238233
const userRepo = req.ctx.repositories.getBaseUserRepository()
@@ -318,15 +313,18 @@ async function updateOrg (req, res, next) {
318313
}
319314
}
320315

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+
321319
updatedOrg = await repo.updateOrgFull(shortName, req.ctx.body, { session }, false, requestingUser.UUID, isAdmin, isSecretariat)
322320
jointApprovalRequired = _.get(updatedOrg, 'joint_approval_required', false)
323321
_.unset(updatedOrg, 'joint_approval_required')
324-
325-
await session.commitTransaction()
326-
session.startTransaction()
327-
// Checking for existing Conversations
328-
const existingConversations = await conversationRepo.getAllByTargetUUID(updatedOrg.UUID, isSecretariat, { session }) || []
329-
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+
}
330328

331329
await session.commitTransaction()
332330
} catch (updateErr) {
@@ -387,7 +385,7 @@ async function updateOrg (req, res, next) {
387385
*/
388386
async function deleteOrg (req, res, next) {
389387
try {
390-
const session = await mongoose.startSession()
388+
const session = await mongoose.startSession({ causalConsistency: false })
391389
const repo = req.ctx.repositories.getBaseOrgRepository()
392390
const shortName = req.ctx.params.identifier
393391

@@ -500,7 +498,7 @@ async function getUsers (req, res, next) {
500498
* Called by POST /api/registryOrg/:shortname/user
501499
*/
502500
async function createUserByOrg (req, res, next) {
503-
const session = await mongoose.startSession()
501+
const session = await mongoose.startSession({ causalConsistency: false })
504502
try {
505503
const body = req.ctx.body
506504
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/repositories/baseOrgRepository.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ class BaseOrgRepository extends BaseRepository {
331331
// In the future we may be able to dynamically detect, but for now we will take a boolean
332332
let legacyObjectRaw = null
333333
let registryObjectRaw = null
334-
let legacyObject = null
335334
let registryObject = null
336335
const legacyOrgRepo = new OrgRepository()
337336
const ReviewObjectRepository = require('./reviewObjectRepository')
@@ -448,8 +447,9 @@ class BaseOrgRepository extends BaseRepository {
448447

449448
// The legacy way of doing this, the way this is written under the hood there is no other way
450449
// This await does not return a value, even though there is a return in it. :shrugg:
450+
let postUpdate = {}
451451
if (isSecretariat) {
452-
await legacyOrgRepo.updateByOrgUUID(sharedUUID, legacyObjectRaw, options)
452+
postUpdate = await legacyOrgRepo.updateByOrgUUID(sharedUUID, legacyObjectRaw, options)
453453
}
454454

455455
// If we are not a secretariat, then we need to return the uuid of the review object.
@@ -459,12 +459,9 @@ class BaseOrgRepository extends BaseRepository {
459459

460460
if (isLegacyObject) {
461461
// This gets us the mongoose object that has all the right data in it, the "legacyObjectRaw" is the custom JSON we are sending. NOT the post written object.
462-
legacyObject = await legacyOrgRepo.findOneByShortName(
463-
legacyObjectRaw.short_name,
464-
options
465-
)
466462
// Convert the actual model, back to a json model
467-
const legacyObjectRawJson = legacyObject.toObject()
463+
464+
const legacyObjectRawJson = postUpdate.toObject()
468465
// Remove private stuff
469466
delete legacyObjectRawJson.__v
470467
delete legacyObjectRawJson._id
@@ -785,11 +782,11 @@ class BaseOrgRepository extends BaseRepository {
785782
updatedRegistryOrg = _.merge(registryOrg, _.omit(registryObjectRaw, jointApprovalFieldsRegistry))
786783
updatedLegacyOrg = _.merge(legacyOrg, _.omit(legacyObjectRaw, jointApprovalFieldsLegacy))
787784
}
788-
789785
// handle conversation
790786
const requestingUser = await userRepo.findUserByUUID(requestingUserUUID, options)
787+
const conversationArray = []
791788
if (conversation) {
792-
await conversationRepo.createConversation(registryOrg.UUID, conversation, requestingUser, isSecretariat, options)
789+
conversationArray.push(await conversationRepo.createConversation(registryOrg.UUID, conversation, requestingUser, isSecretariat, options))
793790
}
794791

795792
// ADD AUDIT ENTRY AUTOMATICALLY for the registry object before it gets saved.
@@ -826,6 +823,7 @@ class BaseOrgRepository extends BaseRepository {
826823
}
827824
console.log('Audit entry created for registry object')
828825
} catch (auditError) {
826+
console.error('Audit entry creation failed:', auditError)
829827
}
830828
}
831829

@@ -878,6 +876,7 @@ class BaseOrgRepository extends BaseRepository {
878876
}
879877

880878
const plainJavascriptRegistryOrg = updatedRegistryOrg.toObject()
879+
plainJavascriptRegistryOrg.conversation = conversationArray
881880
// Remove private things
882881
delete plainJavascriptRegistryOrg.__v
883882
delete plainJavascriptRegistryOrg._id

src/repositories/baseRepository.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class BaseRepository {
5959
}
6060

6161
async findOneAndUpdate (query = {}, set = {}, options = {}) {
62-
return this.collection.findOneAndUpdate(query, set, options)
62+
return this.collection.findOneAndUpdate(query, set, { ...options, new: true })
6363
}
6464

6565
async findOneAndReplace (query = {}, replacement = {}) {

src/repositories/orgRepository.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class OrgRepository extends BaseRepository {
2424
// The filter to find the document
2525
const filter = { UUID: orgUUID }
2626
const updatePayload = { $set: updateData }
27-
return this.collection.findOneAndUpdate(filter, updatePayload, executeOptions)
27+
const data = await this.collection.findOneAndUpdate(filter, updatePayload, { ...executeOptions, new: true })
28+
return data
2829
}
2930

3031
async isSecretariat (org, options = {}) {

0 commit comments

Comments
 (0)