From 6b47ae3ebd967280b93bd465d6e21c063f65d3d0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 28 Sep 2022 16:29:58 +0100 Subject: [PATCH 001/229] filter and split publisher name --- src/resources/filters/filters.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/filters/filters.service.js b/src/resources/filters/filters.service.js index 87491fa8..2033e090 100644 --- a/src/resources/filters/filters.service.js +++ b/src/resources/filters/filters.service.js @@ -183,8 +183,8 @@ export default class FiltersService { Object.keys(filters).forEach(filterKey => { // 9. Set filter values to title case (all except publisher) / upper case (publisher) and remove white space if (filterKey === 'publisher') { - filters[filterKey] = filters[filterKey].map(value => - value.includes('>') ? value.split(' > ')[1].toString().toUpperCase().trim() : value.toString().toUpperCase().trim() + filters[filterKey] = filters[filterKey].map(value => + value.includes('>') ? value.split('>')[1].toString().toUpperCase().trim() : value.toString().toUpperCase().trim() ); } else { filters[filterKey] = filters[filterKey].map(value => (filterKey === 'spatial') ? value.toString().trim() : helper.toTitleCase(value.toString().trim())); From cdceadf1861d545dd6bd7f2eba55155d180fc94e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 14 Oct 2022 15:59:05 +0300 Subject: [PATCH 002/229] main branch for IAM story --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8ef7f6e1..dcfe2c7d 100644 --- a/README.md +++ b/README.md @@ -152,3 +152,6 @@ terraform apply tf_apply && rm tf_apply ``` [Link to terraform file](deployment/GCP/api.tf) + + +... \ No newline at end of file From d0ec8344c8ce85fc16c560b42cc7585f258bcd65 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 26 Oct 2022 12:53:27 +0100 Subject: [PATCH 003/229] removed console log and errors and sentry --- migrations/1620418612003-test_migration.js | 2 +- ...68706696-shared_applications_versioning.js | 2 +- package.json | 2 - .../repositoryPattern/controller.hbs | 2 +- src/config/db.js | 4 +- src/config/server.js | 28 +--- .../datasetonboarding.controller.js | 8 +- src/resources/account/account.route.js | 2 +- .../auth/sso/sso.discourse.router.js | 2 +- src/resources/auth/utils.js | 2 +- .../bpmnworkflow/bpmnworkflow.controller.js | 14 +- src/resources/cohort/cohort.controller.js | 2 +- .../cohortprofiling.controller.js | 6 +- src/resources/course/course.repository.js | 2 +- src/resources/course/v2/course.controller.js | 4 +- .../dataUseRegister.controller.js | 2 +- .../amendment/amendment.controller.js | 4 +- .../datarequest/datarequest.controller.js | 2 +- .../schema/datarequest.schema.controller.js | 2 +- .../schema/datarequest.schemas.route.js | 2 +- .../datarequest/utils/datarequest.util.js | 2 +- src/resources/dataset/dataset.controller.js | 4 +- src/resources/dataset/v1/dataset.route.js | 13 +- src/resources/dataset/v1/dataset.service.js | 137 +++--------------- src/resources/discourse/discourse.route.js | 12 +- src/resources/discourse/discourse.service.js | 18 +-- src/resources/filters/filters.controller.js | 2 +- src/resources/filters/filters.entity.js | 2 +- src/resources/filters/filters.repository.js | 2 +- src/resources/help/help.router.js | 2 +- .../linkchecker/linkchecker.router.js | 13 +- src/resources/message/message.controller.js | 12 +- src/resources/paper/paper.controller.js | 4 +- src/resources/paper/v1/paper.route.js | 2 +- src/resources/person/person.route.js | 2 +- src/resources/project/project.controller.js | 4 +- .../questionbank/questionbank.controller.js | 2 +- .../questionbank/questionbank.service.js | 1 - src/resources/search/search.repository.js | 6 +- .../spatialfilter/LocationController.js | 2 +- src/resources/stats/stats.router.js | 2 +- src/resources/stats/v1/stats.route.js | 2 +- src/resources/team/team.controller.js | 48 +++--- src/resources/tool/data.repository.js | 2 +- src/resources/tool/v1/tool.route.js | 2 +- src/resources/tool/v2/tool.controller.js | 4 +- src/resources/topic/topic.controller.js | 26 ++-- src/resources/user/user.register.route.js | 2 +- src/resources/user/user.route.js | 2 +- .../utilities/emailGenerator.util.js | 26 +--- src/resources/utilities/logger.js | 29 +--- .../utilities/notificationBuilder.js | 2 +- src/resources/workflow/workflow.controller.js | 10 +- src/services/cachePubSub/cachePubSubClient.js | 8 +- src/services/google/PubSubService.js | 4 +- src/services/google/PubSubWithRetryService.js | 2 +- src/services/httpClient/httpClient.js | 6 +- src/services/hubspot/hubspot.js | 11 -- src/services/hubspot/hubspot.route.js | 7 +- src/services/mailchimp/mailchimp.js | 44 +----- src/services/mailchimp/mailchimp.route.js | 7 +- src/utils/datasetonboarding.util.js | 4 +- 62 files changed, 175 insertions(+), 411 deletions(-) diff --git a/migrations/1620418612003-test_migration.js b/migrations/1620418612003-test_migration.js index 459f89ae..fa2441b4 100644 --- a/migrations/1620418612003-test_migration.js +++ b/migrations/1620418612003-test_migration.js @@ -6,7 +6,7 @@ async function up() { // Write migration here //await UserModel.findOneAndUpdate({ email: 'robin.kavanagh@paconsulting.com' }, { firstname: 'robin2' }); - console.log('Sample migration ran successfully'); + process.stdout.write(`Sample migration ran successfully\n`); } /** diff --git a/migrations/1631268706696-shared_applications_versioning.js b/migrations/1631268706696-shared_applications_versioning.js index e0180170..19119202 100644 --- a/migrations/1631268706696-shared_applications_versioning.js +++ b/migrations/1631268706696-shared_applications_versioning.js @@ -26,7 +26,7 @@ async function up() { }, }); } catch (err) { - console.error(err); + process.stdout.write(`Migration error - shared applications versioning: ${err.message}\n`); } }); diff --git a/package.json b/package.json index 45bdd404..a3211fe6 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,6 @@ "@google-cloud/storage": "^5.3.0", "@hubspot/api-client": "^4.1.0", "@sendgrid/mail": "^7.1.0", - "@sentry/node": "^6.4.1", - "@sentry/tracing": "^6.4.1", "ajv": "^8.1.0", "ajv-formats": "^2.0.2", "async": "^3.2.0", diff --git a/plop-templates/repositoryPattern/controller.hbs b/plop-templates/repositoryPattern/controller.hbs index f5d40513..49380d35 100644 --- a/plop-templates/repositoryPattern/controller.hbs +++ b/plop-templates/repositoryPattern/controller.hbs @@ -37,7 +37,7 @@ export default class {{capitalise entityName}}Controller extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`repositoryPatter: ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/config/db.js b/src/config/db.js index f0d67cf1..f889fea1 100644 --- a/src/config/db.js +++ b/src/config/db.js @@ -23,9 +23,9 @@ const connectToDatabase = async () => { bufferMaxEntries: 0, }); - console.log('MongoDB connected...'); + process.stdout.write(`MongoDB connected...\n`); } catch (err) { - console.error(err.message); + process.stdout.write(`connectToDatabase : ${err.message}\n`); process.exit(1); } diff --git a/src/config/server.js b/src/config/server.js index cbdb1bbd..cadb2908 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -10,38 +10,12 @@ import cookieParser from 'cookie-parser'; import bodyParser from 'body-parser'; import { connectToDatabase } from './db'; import { initialiseAuthentication } from '../resources/auth'; -import * as Sentry from '@sentry/node'; -import * as Tracing from '@sentry/tracing'; import helper from '../resources/utilities/helper.util'; require('dotenv').config(); var app = express(); -const readEnv = process.env.ENV || 'prod'; -if (readEnv === 'test' || readEnv === 'prod') { - Sentry.init({ - dsn: 'https://b6ea46f0fbe048c9974718d2c72e261b@o444579.ingest.sentry.io/5653683', - environment: helper.getEnvironment(), - integrations: [ - // enable HTTP calls tracing - new Sentry.Integrations.Http({ tracing: true }), - // enable Express.js middleware tracing - new Tracing.Integrations.Express({ - // trace all requests to the default router - app, - }), - ], - tracesSampleRate: 1.0, - }); - // RequestHandler creates a separate execution context using domains, so that every - // transaction/span/breadcrumb is attached to its own Hub instance - app.use(Sentry.Handlers.requestHandler()); - // TracingHandler creates a trace for every incoming request - app.use(Sentry.Handlers.tracingHandler()); - app.use(Sentry.Handlers.errorHandler()); -} - const Account = require('./account'); const configuration = require('./configuration'); @@ -261,4 +235,4 @@ app.use('/api/v1/locations', require('../resources/spatialfilter/SpatialRouter') initialiseAuthentication(app); // launch our backend into a port -app.listen(API_PORT, () => console.log(`LISTENING ON PORT ${API_PORT}`)); +app.listen(API_PORT, () => process.stdout.write(`LISTENING ON PORT ${API_PORT}\n`)); diff --git a/src/controllers/datasetonboarding.controller.js b/src/controllers/datasetonboarding.controller.js index 260ddabf..98484928 100644 --- a/src/controllers/datasetonboarding.controller.js +++ b/src/controllers/datasetonboarding.controller.js @@ -3,7 +3,6 @@ import _ from 'lodash'; import axios from 'axios'; import FormData from 'form-data'; import { v4 as uuidv4 } from 'uuid'; -import * as Sentry from '@sentry/node'; import { isEmpty, escapeRegExp } from 'lodash'; import { Data } from '../resources/tool/data.model'; @@ -14,8 +13,6 @@ import { PublisherModel } from '../resources/publisher/publisher.model'; import { activityLogService } from '../resources/activitylog/dependency'; const HttpClient = require('../services/httpClient/httpClient'); -const readEnv = process.env.ENV || 'prod'; - export default class DatasetOnboardingController { constructor(datasetonboardingService) { this.datasetonboardingService = datasetonboardingService; @@ -614,10 +611,7 @@ export default class DatasetOnboardingController { return res.status(400).json({ success: false, message: 'No metadata found' }); } } catch (err) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - process.stdout.write(`${err.message}\n`); + process.stdout.write(`DATASETONBOARDING - Bulk upload of metadata failed : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Bulk upload of metadata failed', error: err.message }); } }; diff --git a/src/resources/account/account.route.js b/src/resources/account/account.route.js index f2aba717..b3d36bcf 100644 --- a/src/resources/account/account.route.js +++ b/src/resources/account/account.route.js @@ -169,7 +169,7 @@ router.put('/status', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Ad return res.json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`ACCOUNT - status : ${err.message}\n`); return res.status(500).json({ success: false, error: err }); } }); diff --git a/src/resources/auth/sso/sso.discourse.router.js b/src/resources/auth/sso/sso.discourse.router.js index e7766983..7b0f667d 100644 --- a/src/resources/auth/sso/sso.discourse.router.js +++ b/src/resources/auth/sso/sso.discourse.router.js @@ -19,7 +19,7 @@ router.get('/', function (req, res, next) { try { redirectUrl = discourseLogin(req.query.sso, req.query.sig, req.user); } catch (err) { - console.error(err.message); + process.stdout.write(`Single Sign On for Discourse forum : ${err.message}\n`); return res.status(500).send('Error authenticating the user.'); } } diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index b3e1ff55..380d9243 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -178,7 +178,7 @@ const loginAndSignToken = (req, res, next) => { try { redirectUrl = discourseLogin(queryStringParsed.sso, queryStringParsed.sig, req.user); } catch (err) { - console.error(err.message); + process.stdout.write(`UTILS - loginAndSignToken : ${err.message}\n`); return res.status(500).send('Error authenticating the user.'); } } diff --git a/src/resources/bpmnworkflow/bpmnworkflow.controller.js b/src/resources/bpmnworkflow/bpmnworkflow.controller.js index d02fda88..f1272746 100644 --- a/src/resources/bpmnworkflow/bpmnworkflow.controller.js +++ b/src/resources/bpmnworkflow/bpmnworkflow.controller.js @@ -48,7 +48,7 @@ module.exports = { businessKey: businessKey.toString(), }; await axios.post(`${bpmnBaseUrl}/engine-rest/process-definition/key/GatewayWorkflowSimple/start`, data, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postCreateProcess : ${err.message}\n`); }); }, @@ -80,7 +80,7 @@ module.exports = { }, }; await axios.post(`${bpmnBaseUrl}/engine-rest/task/${taskId}/complete`, data, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postUpdateProcess : ${err.message}\n`); }); }, @@ -106,7 +106,7 @@ module.exports = { businessKey: businessKey.toString(), }; await axios.post(`${bpmnBaseUrl}/engine-rest/process-definition/key/GatewayReviewWorkflowComplex/start`, data, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postStartPreReview : ${err.message}\n`); }); }, @@ -134,7 +134,7 @@ module.exports = { }, }; await axios.post(`${bpmnBaseUrl}/engine-rest/task/${taskId}/complete`, data, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postStartManagerReview : ${err.message}\n`); }); }, @@ -142,7 +142,7 @@ module.exports = { // Manager has approved sectoin let { businessKey } = bpmContext; await axios.post(`${bpmnBaseUrl}/api/gateway/workflow/v1/manager/completed/${businessKey}`, bpmContext.config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postManagerApproval : ${err.message}\n`); }); }, @@ -150,7 +150,7 @@ module.exports = { //Start Step-Review process let { businessKey } = bpmContext; await axios.post(`${bpmnBaseUrl}/api/gateway/workflow/v1/complete/review/${businessKey}`, bpmContext, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postStartStepReview : ${err.message}\n`); }); }, @@ -158,7 +158,7 @@ module.exports = { //Start Next-Step process let { businessKey } = bpmContext; await axios.post(`${bpmnBaseUrl}/api/gateway/workflow/v1/reviewer/complete/${businessKey}`, bpmContext, config).catch(err => { - console.error(err.message); + process.stdout.write(`BPMN - postCompleteReview : ${err.message}\n`); }); }, }; diff --git a/src/resources/cohort/cohort.controller.js b/src/resources/cohort/cohort.controller.js index d0c56ccc..a802f5d1 100644 --- a/src/resources/cohort/cohort.controller.js +++ b/src/resources/cohort/cohort.controller.js @@ -37,7 +37,7 @@ export default class CohortController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`COHORT - getCohort : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/cohortprofiling/cohortprofiling.controller.js b/src/resources/cohortprofiling/cohortprofiling.controller.js index 819c7445..06855ccd 100644 --- a/src/resources/cohortprofiling/cohortprofiling.controller.js +++ b/src/resources/cohortprofiling/cohortprofiling.controller.js @@ -35,7 +35,7 @@ export default class CohortProfilingController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`COHORT - getCohortProfilingByVariable : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -52,7 +52,7 @@ export default class CohortProfilingController extends Controller { // 2. Return Cohort Profiling data return res.status(200).json({ success: true, cohortProfiling }); } catch (err) { - console.error(err.message); + process.stdout.write(`COHORT - getCohortProfiling : ${err.message}\n`); return res.status(500).json({ success: false, message: err.message }); } } @@ -86,7 +86,7 @@ export default class CohortProfilingController extends Controller { // Return Cohort Profiling data return res.status(200).json({ success: true, cohortProfiling }); } catch (err) { - console.error(err.message); + process.stdout.write(`COHORT - saveCohortProfiling : ${err.message}\n`); return res.status(500).json({ success: false, message: err.message }); } } diff --git a/src/resources/course/course.repository.js b/src/resources/course/course.repository.js index 0cee771b..932cc392 100644 --- a/src/resources/course/course.repository.js +++ b/src/resources/course/course.repository.js @@ -319,7 +319,7 @@ const setStatus = async req => { resolve(id); } catch (err) { - console.error(err.message); + process.stdout.write(`COURSE - setStatus : ${err.message}\n`); reject(new Error(err)); } }); diff --git a/src/resources/course/v2/course.controller.js b/src/resources/course/v2/course.controller.js index f337f0bd..7d19a7df 100644 --- a/src/resources/course/v2/course.controller.js +++ b/src/resources/course/v2/course.controller.js @@ -33,7 +33,7 @@ export default class CourseController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`COURSE - setStatus : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -52,7 +52,7 @@ export default class CourseController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`COURSE - getCourses : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index 03903dfe..a50fc24e 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -101,7 +101,7 @@ export default class DataUseRegisterController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`DATA USE REGISTER - getDataUseRegister : ${err.message}`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/datarequest/amendment/amendment.controller.js b/src/resources/datarequest/amendment/amendment.controller.js index 2347b26a..e065195f 100644 --- a/src/resources/datarequest/amendment/amendment.controller.js +++ b/src/resources/datarequest/amendment/amendment.controller.js @@ -106,7 +106,7 @@ export default class AmendmentController extends Controller { // 9. Save changes to database await accessRecord.save(async err => { if (err) { - console.error(err.message); + process.stdout.write(`AMENDMENT - setAmendment : ${err.message}\n`); return res.status(500).json({ status: 'error', message: err.message }); } else { // 10. Update json schema and question answers with modifications since original submission and retain previous version requested updates @@ -247,7 +247,7 @@ export default class AmendmentController extends Controller { // 9. Save changes to database await accessRecord.save(async err => { if (err) { - console.error(err.message); + process.stdout.write(`AMENDMENT - requestAmendments : ${err.message}\n`); return res.status(500).json({ status: 'error', message: err.message }); } else { // 10. Send update request notifications diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 2f4cb631..35e7641e 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -1378,7 +1378,7 @@ export default class DataRequestController extends Controller { // 8. Return successful response return res.status(200).json({ status: 'success' }); } catch (err) { - console.error(err.message); + process.stdout.write(`DATA REQUEST - updateAccessRequestDeleteFile : ${err.message}\n`); res.status(500).json({ status: 'error', message: err.message }); } } diff --git a/src/resources/datarequest/schema/datarequest.schema.controller.js b/src/resources/datarequest/schema/datarequest.schema.controller.js index dd7add23..6c31f3f4 100644 --- a/src/resources/datarequest/schema/datarequest.schema.controller.js +++ b/src/resources/datarequest/schema/datarequest.schema.controller.js @@ -36,7 +36,7 @@ export default class DatarequestschemaController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`DATA REQUEST - getDatarequestschema : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/datarequest/schema/datarequest.schemas.route.js b/src/resources/datarequest/schema/datarequest.schemas.route.js index 4cf5698e..3a300a5f 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.route.js +++ b/src/resources/datarequest/schema/datarequest.schemas.route.js @@ -105,6 +105,6 @@ async function archiveOtherVersions(id, dataSetId, status) { ); } } catch (err) { - console.error(err.message); + process.stdout.write(`DATA REQUEST - archiveOtherVersions : ${err.message}\n`); } } diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index b6020cdd..a7abd043 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -60,7 +60,7 @@ const getUserPermissionsForApplication = (application, userId, _id) => { } return { authorised, userType }; } catch (err) { - console.error(err.message); + process.stdout.write(`DATA REQUEST - getUserPermissionsForApplication : ${err.message}\n`); return { authorised: false, userType: '' }; } }; diff --git a/src/resources/dataset/dataset.controller.js b/src/resources/dataset/dataset.controller.js index e8fbf990..f480dae0 100644 --- a/src/resources/dataset/dataset.controller.js +++ b/src/resources/dataset/dataset.controller.js @@ -28,7 +28,7 @@ export default class DatasetController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`DATA SET - getDataset : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -48,7 +48,7 @@ export default class DatasetController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`DATA SET - getDatasets : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/dataset/v1/dataset.route.js b/src/resources/dataset/v1/dataset.route.js index 63a09df5..6893bb97 100644 --- a/src/resources/dataset/v1/dataset.route.js +++ b/src/resources/dataset/v1/dataset.route.js @@ -7,7 +7,6 @@ import escape from 'escape-html'; import { Course } from '../../course/course.model'; import { DataUseRegister } from '../../dataUseRegister/dataUseRegister.model'; import { filtersService } from '../../filters/dependency'; -import * as Sentry from '@sentry/node'; const router = express.Router(); const rateLimit = require('express-rate-limit'); @@ -17,8 +16,6 @@ const datasetLimiter = rateLimit({ message: 'Too many calls have been made to this api from this IP, please try again after an hour', }); -const readEnv = process.env.ENV || 'prod'; - router.post('/', async (req, res) => { try { // Check to see if header is in json format @@ -46,10 +43,7 @@ router.post('/', async (req, res) => { // Return response indicating job has started (do not await async import) return res.status(200).json({ success: true, message: 'Caching started' }); } catch (err) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(err.message); + process.stdout.write(`DATASET - Caching failed : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Caching failed' }); } }); @@ -78,10 +72,7 @@ router.post('/updateServices', async (req, res) => { return res.status(200).json({ success: true, message: 'Services Update started' }); } catch (err) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(err.message); + process.stdout.write(`DATASET - Services update failed : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Services update failed' }); } }); diff --git a/src/resources/dataset/v1/dataset.service.js b/src/resources/dataset/v1/dataset.service.js index 3d28c2be..e03b836d 100644 --- a/src/resources/dataset/v1/dataset.service.js +++ b/src/resources/dataset/v1/dataset.service.js @@ -1,7 +1,6 @@ import { Data } from '../../tool/data.model'; import { MetricsData } from '../../stats/metrics.model'; import axios from 'axios'; -import * as Sentry from '@sentry/node'; import { v4 as uuidv4 } from 'uuid'; import { filtersService } from '../../filters/dependency'; import { PublisherModel } from '../../publisher/publisher.model'; @@ -16,8 +15,6 @@ let metadataQualityList = [], datasetsMDCIDs = [], counter = 0; -const readEnv = process.env.ENV || 'prod'; - export async function updateExternalDatasetServices(services) { for (let service of services) { if (service === 'phenotype') { @@ -26,34 +23,18 @@ export async function updateExternalDatasetServices(services) { timeout: 10000, }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get metadata quality value ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get metadata quality value ' + err.message); + process.stdout.write(`DATASET - Unable to get metadata quality value : ${err.message}`); }); for (const pid in phenotypesList.data) { await Data.updateMany({ pid: pid }, { $set: { 'datasetfields.phenotypes': phenotypesList.data[pid] } }); - console.log(`PID is ${pid} and number of phenotypes is ${phenotypesList.data[pid].length}`); + process.stdout.write(`PID is ${pid} and number of phenotypes is ${phenotypesList.data[pid].length}`); } } else if (service === 'dataUtility') { const dataUtilityList = await axios .get('https://raw.githubusercontent.com/HDRUK/datasets/master/reports/data_utility.json', { timeout: 10000 }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get data utility ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get data utility ' + err.message); + process.stdout.write(`DATASET - Unable to get data utility : ${err.message}`); }); for (const dataUtility of dataUtilityList.data) { @@ -70,7 +51,7 @@ export async function updateExternalDatasetServices(services) { await dataset.save(); } // log details - // console.log(`DatasetID is ${dataUtility.id} and metadata richness is ${dataUtility.metadata_richness}`); + // process.stdout.write(`DatasetID is ${dataUtility.id} and metadata richness is ${dataUtility.metadata_richness}`); } } } @@ -92,7 +73,7 @@ export async function importCatalogues(cataloguesToImport, override = false, lim } const isValid = validateCatalogueParams(metadataCatalogues[catalogue]); if (!isValid) { - console.error('Catalogue failed to run due to incorrect or incomplete parameters'); + process.stdout.write(`Catalogue failed to run due to incorrect or incomplete parameters`); continue; } const { metadataUrl, dataModelExportRoute, username, password, source, instanceType } = metadataCatalogues[catalogue]; @@ -181,7 +162,7 @@ function initialiseImporter() { async function importMetadataFromCatalogue(baseUri, dataModelExportRoute, source, { instanceType, credentials, override = false, limit }) { const startCacheTime = Date.now(); - console.log( + process.stdout.write( `Starting metadata import for ${source} on ${instanceType} at ${Date()} with base URI ${baseUri}, override:${override}, limit:${ limit || 'all' }` @@ -199,21 +180,13 @@ async function importMetadataFromCatalogue(baseUri, dataModelExportRoute, source await logoutCatalogue(baseUri); await loginCatalogue(baseUri, credentials); await loadDatasets(baseUri, dataModelExportRoute, datasetsMDCList.items, datasetsMDCList.count, source, limit).catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: `Unable to complete the metadata import for ${source} ${err.message}`, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error(`Unable to complete the metadata import for ${source} ${err.message}`); + process.stdout.write(`DATASET - Unable to complete the metadata import for ${source} ${err.message}`); }); await logoutCatalogue(baseUri); await archiveMissingDatasets(source); const totalCacheTime = ((Date.now() - startCacheTime) / 1000).toFixed(3); - console.log(`Run Completed for ${source} at ${Date()} - Run took ${totalCacheTime}s`); + process.stdout.write(`Run Completed for ${source} at ${Date()} - Run took ${totalCacheTime}s`); } async function loadDatasets(baseUri, dataModelExportRoute, datasetsToImport, datasetsToImportCount, source, limit) { @@ -223,7 +196,7 @@ async function loadDatasets(baseUri, dataModelExportRoute, datasetsToImport, dat } for (const datasetMDC of datasetsToImport) { counter++; - console.log(`Starting ${counter} of ${datasetsToImportCount} datasets (${datasetMDC.id})`); + process.stdout.write(`Starting ${counter} of ${datasetsToImportCount} datasets (${datasetMDC.id})`); let datasetHDR = await Data.findOne({ datasetid: datasetMDC.id }); datasetsMDCIDs.push({ datasetid: datasetMDC.id }); @@ -240,46 +213,22 @@ async function loadDatasets(baseUri, dataModelExportRoute, datasetsToImport, dat timeout: 60000, }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get dataset JSON ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get metadata JSON ' + err.message); + process.stdout.write(`DATASET - Unable to get metadata JSON : ${err.message}`); }); const elapsedTime = ((Date.now() - startImportTime) / 1000).toFixed(3); - console.log(`Time taken to import JSON ${elapsedTime} (${datasetMDC.id})`); + process.stdout.write(`Time taken to import JSON ${elapsedTime} (${datasetMDC.id})`); const metadataSchemaCall = axios //Paul - Remove and populate gateway side .get(`${baseUri}/api/profiles/uk.ac.hdrukgateway/HdrUkProfilePluginService/schema.org/${datasetMDC.id}`, { timeout: 10000, }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get metadata schema ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get metadata schema ' + err.message); + process.stdout.write(`DATASET - Unable to get metadata schema : ${err.message}`); }); const versionLinksCall = axios.get(`${baseUri}/api/catalogueItems/${datasetMDC.id}/semanticLinks`, { timeout: 10000 }).catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get version links ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get version links ' + err.message); + process.stdout.write(`DATASET - Unable to get version links : ${err.message}`); }); const [metadataSchema, versionLinks] = await axios.all([metadataSchemaCall, versionLinksCall]); @@ -404,7 +353,7 @@ async function loadDatasets(baseUri, dataModelExportRoute, datasetsToImport, dat datasetv2: datasetv2Object, } ); - console.log(`Dataset Editted (${datasetMDC.id})`); + process.stdout.write(`Dataset Editted (${datasetMDC.id})`); } else { //Add let uuid = uuidv4(); @@ -488,10 +437,10 @@ async function loadDatasets(baseUri, dataModelExportRoute, datasetsToImport, dat data.datasetfields.phenotypes = phenotypes; data.datasetv2 = datasetv2Object; await data.save(); - console.log(`Dataset Added (${datasetMDC.id})`); + process.stdout.write(`Dataset Added (${datasetMDC.id})`); } - console.log(`Finished ${counter} of ${datasetsToImportCount} datasets (${datasetMDC.id})`); + process.stdout.write(`Finished ${counter} of ${datasetsToImportCount} datasets (${datasetMDC.id})`); } } @@ -505,15 +454,7 @@ async function getDataUtilityExport() { return await axios .get('https://raw.githubusercontent.com/HDRUK/datasets/master/reports/data_utility.json', { timeout: 10000 }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get data utility ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get data utility ' + err.message); + process.stdout.write(`DATASET - Unable to get data utility : ${err.message}`); }); } @@ -527,15 +468,7 @@ async function getPhenotypesExport() { return await axios .get('https://raw.githubusercontent.com/spiros/hdr-caliber-phenome-portal/master/_data/dataset2phenotypes.json', { timeout: 10000 }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get metadata quality value ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get metadata quality value ' + err.message); + process.stdout.write(`DATASET - Unable to get metadata quality value : ${err.message}`); }); } @@ -549,15 +482,7 @@ async function getMetadataQualityExport() { return await axios .get('https://raw.githubusercontent.com/HDRUK/datasets/master/reports/metadata_quality.json', { timeout: 10000 }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'Unable to get metadata quality value ' + err.message, - level: Sentry.Severity.Error, - }); - Sentry.captureException(err); - } - console.error('Unable to get metadata quality value ' + err.message); + process.stdout.write(`DATASET Unable to get metadata quality value ${err.message}`); }); } @@ -569,14 +494,7 @@ async function getDataModels(baseUri) { resolve(response.data); }) .catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: 'The caching run has failed because it was unable to get a count from the MDC', - level: Sentry.Severity.Fatal, - }); - Sentry.captureException(err); - } + process.stdout.write(`DATASET - The caching run has failed because it was unable to get a count from the MDC : ${err.message}\n`); reject(err); }); }).catch(() => { @@ -589,16 +507,9 @@ async function checkDifferentialValid(incomingMetadataCount, source, override) { const datasetsHDRCount = await Data.countDocuments({ type: 'dataset', activeflag: 'active', source }); if ((incomingMetadataCount / datasetsHDRCount) * 100 < 90 && !override) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Caching', - message: `The caching run has failed because the counts from the MDC (${incomingMetadataCount}) where ${ - 100 - (incomingMetadataCount / datasetsHDRCount) * 100 - }% lower than the number stored in the DB (${datasetsHDRCount})`, - level: Sentry.Severity.Fatal, - }); - Sentry.captureException(); - } + process.stdout.write(`DATASET - checkDifferentialValid : The caching run has failed because the counts from the MDC (${incomingMetadataCount}) where ${ + 100 - (incomingMetadataCount / datasetsHDRCount) * 100 + }% lower than the number stored in the DB (${datasetsHDRCount})\n`); return false; } return true; @@ -611,7 +522,7 @@ async function getDataAccessRequestCustodians() { async function logoutCatalogue(baseUri) { await axios.post(`${baseUri}/api/authentication/logout`, { withCredentials: true, timeout: 10000 }).catch(err => { - console.error(`Error when trying to logout of the MDC - ${err.message}`); + process.stdout.write(`DATASET - Error when trying to logout of the MDC : ${err.message}`); }); } diff --git a/src/resources/discourse/discourse.route.js b/src/resources/discourse/discourse.route.js index 5d5da363..072a9162 100644 --- a/src/resources/discourse/discourse.route.js +++ b/src/resources/discourse/discourse.route.js @@ -39,7 +39,7 @@ router.get('/topic/:topicId', async (req, res) => { return res.status(500).json({ success: false, error: error.message }); }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - GET TOPIC: ${err.message}`); return res.status(500).json({ success: false, error: 'Error retrieving the topic, please try again later...' }); } }); @@ -67,7 +67,7 @@ router.get('/user/topic/:topicId', passport.authenticate('jwt'), utils.checkIsIn return res.status(500).json({ success: false, error: error.message }); }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - GET TOPIC: ${err.message}`); return res.status(500).json({ success: false, error: 'Error retrieving the topic, please try again later...' }); } }); @@ -99,7 +99,7 @@ router.put('/tool/:toolId', passport.authenticate('jwt'), utils.checkIsInRole(RO return res.status(500).json({ success: false, error: error.message }); }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - PUT TOPIC: ${err.message}`); return res.status(500).json({ success: false, error: 'Error creating the topic, please try again later...' }); } }); @@ -167,7 +167,7 @@ router.post('/user/posts', passport.authenticate('jwt'), utils.checkIsInRole(ROL return res.json({ success: true, topic }); } } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - POSTS: ${err.message}`); return res.status(500).json({ success: false, error: 'Error creating the topic, please try again later...' }); } }); @@ -194,7 +194,7 @@ router.put('/user/posts/:postId', passport.authenticate('jwt'), utils.checkIsInR // 5. Return the topic data return res.json({ success: true, topic }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - PUT POSTS: ${err.message}`); return res.status(500).json({ success: false, error: 'Error editing the post, please try again later...' }); } }); @@ -218,7 +218,7 @@ router.delete('/user/posts/:postId', passport.authenticate('jwt'), utils.checkIs return res.status(500).json({ success: false, error: err.message }); }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - DELETE POSTS: ${err.message}`); return res.status(500).json({ success: false, error: 'Error deleting the topic, please try again later...' }); } }); diff --git a/src/resources/discourse/discourse.service.js b/src/resources/discourse/discourse.service.js index 2981bbf2..dba6969d 100644 --- a/src/resources/discourse/discourse.service.js +++ b/src/resources/discourse/discourse.service.js @@ -42,7 +42,7 @@ export async function getDiscourseTopic(topicId, user) { posts: posts, }; } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - getDiscourseTopic : ${err.message}`); } } @@ -109,7 +109,7 @@ export async function createDiscourseTopic(tool) { } } } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - createDiscourseTopic : ${err.message}`); } } @@ -140,7 +140,7 @@ export async function createDiscoursePost(topicId, comment, user) { try { await axios.post(`${process.env.DISCOURSE_URL}/posts.json`, payload, config); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - createDiscoursePost : ${err.message}`); } } @@ -176,7 +176,7 @@ export async function updateDiscoursePost(postId, comment, user) { // 4. Return the post data return post; } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - updateDiscoursePost : ${err.message}`); } } @@ -208,7 +208,7 @@ export async function deleteDiscoursePost(postId, user) { try { await axios.delete(`${process.env.DISCOURSE_URL}/posts/${postId}`, config); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - deleteDiscoursePost : ${err.message}`); } } @@ -248,7 +248,7 @@ async function createUser({ id, email, username }) { // 6. Return the new user object from Discourse return res.data; } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - createUser : ${err.message}`); } } @@ -286,7 +286,7 @@ async function generateAPIKey(discourseUsername) { // 3. Return key return key; } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - generateAPIKey : ${err.message}`); return ''; } } @@ -326,7 +326,7 @@ async function getCredentials(user, strict) { // 6. Update MongoDb to contain users Discourse credentials await UserModel.findOneAndUpdate({ id: { $eq: id } }, { $set: { discourseUsername, discourseKey } }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - getCredentials : ${err.message}`); } // 3. If user has username but no API key, generate new one } else if (_.isEmpty(discourseKey)) { @@ -336,7 +336,7 @@ async function getCredentials(user, strict) { // 5. Update MongoDb to contain users Discourse credentials await UserModel.findOneAndUpdate({ id: { $eq: id } }, { $set: { discourseUsername, discourseKey } }); } catch (err) { - console.error(err.message); + process.stdout.write(`DISCOURSE - getCredentials : ${err.message}`); } } // Return identification payload of registered Discourse user diff --git a/src/resources/filters/filters.controller.js b/src/resources/filters/filters.controller.js index 7f5cc2ba..8607e3f5 100644 --- a/src/resources/filters/filters.controller.js +++ b/src/resources/filters/filters.controller.js @@ -33,7 +33,7 @@ export default class FiltersController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`DISCOURSE - getFilters : ${err.message}`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/filters/filters.entity.js b/src/resources/filters/filters.entity.js index 88d86e58..22ef7746 100644 --- a/src/resources/filters/filters.entity.js +++ b/src/resources/filters/filters.entity.js @@ -12,7 +12,7 @@ export default class FiltersClass extends Entity { mapDto() { if (!this.id) { - console.error('Failed to load filters'); + process.stdout.write(`Failed to load filters`); return; } diff --git a/src/resources/filters/filters.repository.js b/src/resources/filters/filters.repository.js index 38081faf..05d03f55 100644 --- a/src/resources/filters/filters.repository.js +++ b/src/resources/filters/filters.repository.js @@ -15,7 +15,7 @@ export default class FiltersRepository extends Repository { async updateFilterSet(filters, type) { await Filters.findOneAndUpdate({ id: type }, { keys: filters }, { upsert: true }, err => { if (err) { - console.error(err.message); + process.stdout.write(`FILTERS - updateFilterSet : ${err.message}\n`); } }); } diff --git a/src/resources/help/help.router.js b/src/resources/help/help.router.js index a76d8d3b..2b339885 100644 --- a/src/resources/help/help.router.js +++ b/src/resources/help/help.router.js @@ -20,7 +20,7 @@ router.get('/:category', async (req, res) => { // 4. Return help data in response return res.status(200).json({ success: true, help }); } catch (err) { - console.error(err.message); + process.stdout.write(`HELP ROUTER - GET CATEGORY : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred searching for help data', diff --git a/src/resources/linkchecker/linkchecker.router.js b/src/resources/linkchecker/linkchecker.router.js index 2cb2f4e9..08c71996 100644 --- a/src/resources/linkchecker/linkchecker.router.js +++ b/src/resources/linkchecker/linkchecker.router.js @@ -1,5 +1,4 @@ import express from 'express'; -import * as Sentry from '@sentry/node'; import { getObjectResult } from './linkchecker.repository'; import { getUserByUserId } from '../user/user.repository'; import { Data } from '../tool/data.model'; @@ -8,7 +7,6 @@ import _ from 'lodash'; const sgMail = require('@sendgrid/mail'); const hdrukEmail = `enquiry@healthdatagateway.org`; -const readEnv = process.env.ENV || 'prod'; const axios = require('axios'); const router = express.Router(); @@ -104,7 +102,7 @@ router.post('/', async (req, res) => { if (checkUser[0].emailNotifications === true) { let msg = { to: user.email, - from: `${hdrukEmail}`, + from: hdrukEmail, subject: `Updates required for links in ${item.name}.`, html: `${user.firstname} ${user.lastname},

Please review your ${item.type} "${item.name}" here: ${resourceLink}. This ${item.type} contains stale links which require updating. @@ -112,14 +110,7 @@ router.post('/', async (req, res) => { }; await sgMail.send(msg, false, err => { - if (err && (readEnv === 'test' || readEnv === 'prod')) { - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(err); - } + process.stdout.write(`LINKCHECKER - sendEmailToUsers: error`); }); } } diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index c8814598..d5730a78 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -48,13 +48,13 @@ module.exports = { ({ publisher = '' } = tools[0]); if (_.isEmpty(publisher)) { - console.error(`No publisher associated to this dataset`); + process.stdout.write(`No publisher associated to this dataset\n`); return res.status(500).json({ success: false, message: 'No publisher associated to this dataset' }); } // 5. get team ({ team = [] } = publisher); if (_.isEmpty(team)) { - console.error(`No team associated to publisher, cannot message`); + process.stdout.write(`No team associated to publisher, cannot message\n`); return res.status(500).json({ success: false, message: 'No team associated to publisher, cannot message' }); } // 6. Set user type (if found in team, they are custodian) @@ -229,7 +229,7 @@ module.exports = { return res.status(201).json({ success: true, messageObj }); } catch (err) { - console.error(err.message); + process.stdout.write(`MESSAGE - createMessage : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -261,7 +261,7 @@ module.exports = { // 8. Return successful response return res.status(204).json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`MESSAGE - deleteMessage : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -296,7 +296,7 @@ module.exports = { // 6. Return success no content return res.status(204).json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`MESSAGE - updateMessage : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -322,7 +322,7 @@ module.exports = { // 3. Return the number of unread messages return res.status(200).json({ success: true, count: unreadMessageCount }); } catch (err) { - console.error(err.message); + process.stdout.write(`MESSAGE - getUnreadMessageCount : ${err.message}\n`); return res.status(500).json(err.message); } }, diff --git a/src/resources/paper/paper.controller.js b/src/resources/paper/paper.controller.js index 9fd61b73..fada56b9 100644 --- a/src/resources/paper/paper.controller.js +++ b/src/resources/paper/paper.controller.js @@ -33,7 +33,7 @@ export default class PaperController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`PAPER - getPaper : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -52,7 +52,7 @@ export default class PaperController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`PAPER - getPaper : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/paper/v1/paper.route.js b/src/resources/paper/v1/paper.route.js index 4d083956..915006c6 100644 --- a/src/resources/paper/v1/paper.route.js +++ b/src/resources/paper/v1/paper.route.js @@ -71,7 +71,7 @@ router.post('/validate', passport.authenticate('jwt'), async (req, res) => { // 5. Otherwise return valid return res.status(200).json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`PAPER - validate : ${err.message}\n`); return res.status(500).json({ success: false, error: 'Paper link validation failed' }); } }); diff --git a/src/resources/person/person.route.js b/src/resources/person/person.route.js index 564ffca4..b2138a8d 100644 --- a/src/resources/person/person.route.js +++ b/src/resources/person/person.route.js @@ -164,7 +164,7 @@ router.get('/profile/:id', async (req, res) => { let data = [person]; return res.json({ success: true, data: data }); } catch (err) { - console.error(err.message); + process.stdout.write(`PERSON - GET PROFILE : ${err.message}\n`); return res.json({ success: false, error: err.message }); } }); diff --git a/src/resources/project/project.controller.js b/src/resources/project/project.controller.js index c96eb2ad..fc6234c7 100644 --- a/src/resources/project/project.controller.js +++ b/src/resources/project/project.controller.js @@ -33,7 +33,7 @@ export default class ProjectController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`PROJECT - GET PROJECT : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -52,7 +52,7 @@ export default class ProjectController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`PROJECT - GET PROJECTS : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index f6caff38..3b9c7709 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -21,7 +21,7 @@ export default class QuestionbankController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`QUESTIONBANK - GET QUESTIONBANK : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 6bc93e8d..2ef58043 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,4 +1,3 @@ -import { Console } from '@sentry/node/dist/integrations'; import { isEmpty, has } from 'lodash'; export default class QuestionbankService { diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index 5f6a78f5..e241d15b 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -493,14 +493,14 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, const searchResults = type === 'dataUseRegister' ? await collection.aggregate(queryObject).catch(err => { - console.log(err); + process.stdout.write(`${err.message}\n`); }) : await collection .aggregate(queryObject) .skip(parseInt(startIndex)) .limit(parseInt(maxResults)) .catch(err => { - console.log(err); + process.stdout.write(`${err.message}\n`); }); return { data: searchResults }; @@ -939,7 +939,7 @@ export function getObjectFilters(searchQueryStart, queryParams, type) { } } } catch (err) { - console.error(err.message); + process.stdout.write(`SEARCH - GET OBJECT FILTERS : ${err.message}\n`); } } return searchQuery; diff --git a/src/resources/spatialfilter/LocationController.js b/src/resources/spatialfilter/LocationController.js index a7ef7f0e..905cd15e 100644 --- a/src/resources/spatialfilter/LocationController.js +++ b/src/resources/spatialfilter/LocationController.js @@ -11,7 +11,7 @@ class LocationController extends BaseController { async getData(req, res) { const { filter } = req.params; const table = `${process.env.BIG_QUERY_PROJECT_ID}.${process.env.BIG_QUERY_DATABASE}.${process.env.BIG_QUERY_TABLE}`; - console.log(table); + process.stdout.write(`LocationController - getData : ${table}\n`); const statement = `SELECT name, country, level_one, level_two, level_three FROM \`${table}\` WHERE lower(\`name\`) LIKE "%${filter.toLowerCase()}%" diff --git a/src/resources/stats/stats.router.js b/src/resources/stats/stats.router.js index fa2430f9..8718f92a 100644 --- a/src/resources/stats/stats.router.js +++ b/src/resources/stats/stats.router.js @@ -502,7 +502,7 @@ router.get('', async (req, res) => { } } } catch (err) { - console.error(err.message); + process.stdout.write(`STATS - GET STATS : ${err.message}\n`); return res.json({ success: false, error: err.message }); } }); diff --git a/src/resources/stats/v1/stats.route.js b/src/resources/stats/v1/stats.route.js index 4e339078..c46e5ca4 100644 --- a/src/resources/stats/v1/stats.route.js +++ b/src/resources/stats/v1/stats.route.js @@ -90,7 +90,7 @@ router.get('', logger.logRequestMiddleware({ logCategory, action: 'Viewed stats' } return result; } catch (err) { - console.error(err.message); + process.stdout.write(`STATS : ${err.message}\n`); return res.json({ success: false, error: err.message }); } }); diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 6a508026..be01d022 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -33,7 +33,7 @@ const getTeamById = async (req, res) => { // 4. Return team return res.status(200).json({ success: true, team }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - getTeamById : ${err.message}\n`); return res.status(500).json(err.message); } }; @@ -67,7 +67,7 @@ const getTeamMembers = async (req, res) => { // 6. Return team members return res.status(200).json({ success: true, members: users }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - getTeamMembers : ${err.message}\n`); return res.status(500).json(err.message); } }; @@ -146,7 +146,7 @@ const addTeamMembers = async (req, res) => { // 9. Save members handling error callback if validation fails team.save(async err => { if (err) { - console.error(err.message); + process.stdout.write(`TEAM - addTeamMembers : ${err.message}\n`); return res.status(400).json({ success: false, message: err.message, @@ -173,7 +173,7 @@ const addTeamMembers = async (req, res) => { } }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - addTeamMembers : ${err.message}\n`); return res.status(400).json({ success: false, message: 'You must supply a valid team identifier', @@ -226,7 +226,7 @@ const getTeamNotifications = async (req, res) => { // 8. return 200 success return res.status(200).json(notifications); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - getTeamNotifications : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred retrieving team notifications', @@ -435,7 +435,7 @@ const updateNotifications = async (req, res) => { // 13. return 201 with new team return res.status(201).json(team); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - updateNotifications : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred updating team notifications', @@ -463,11 +463,11 @@ const updateNotificationMessages = async (req, res) => { return res.status(201).json(); }) .catch(err => { - console.log(err); + process.stdout.write(`TEAM - updateNotificationMessages : ${err.message}\n`); res.status(500).json({ success: false, message: err.message }); }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - updateNotificationMessages : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred updating notification messages', @@ -529,7 +529,7 @@ const deleteTeamMember = async (req, res) => { team.members = updatedMembers; team.save(function (err) { if (err) { - console.error(err.message); + process.stdout.write(`TEAM - deleteTeamMember : ${err.message}\n`); return res.status(400).json({ success: false, message: err.message, @@ -545,7 +545,7 @@ const deleteTeamMember = async (req, res) => { } }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - deleteTeamMember : ${err.message}\n`); res.status(500).json({ status: 'error', message: err.message }); } }; @@ -592,7 +592,7 @@ const getTeamsList = async (req, res) => { // 4. Return team return res.status(200).json({ success: true, teams }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - getTeamsList : ${err.message}\n`); return res.status(500).json(err.message); } }; @@ -665,23 +665,23 @@ const addTeam = async (req, res) => { timeout: 60000, }) .then(async res => { - console.log(`public flag res: ${res}`); + process.stdout.write(`TEAM - public flag res: ${res}`); }) .catch(err => { - console.error('Error when making folder public on the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when making folder public on the MDC : ${err.message}\n`); }); }) .catch(err => { - console.error('Error when trying to create new folder on the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to create new folder on the MDC : ${err.message}\n`); }); }) .catch(err => { - console.error('Error when trying to login to MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to login to MDC : ${err.message}\n`); }); // 7. Log out of MDC await axios.post(metadataCatalogueLink + `/api/authentication/logout`, { withCredentials: true, timeout: 5000 }).catch(err => { - console.error('Error when trying to logout of the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to logout of the MDC : ${err.message}\n`); }); // 8. If a MDC folder with the name already exists return unsuccessful @@ -727,7 +727,7 @@ const addTeam = async (req, res) => { return res.status(200).json(newPublisher); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - addTeam : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Error', @@ -846,7 +846,7 @@ const editTeam = async (req, res) => { } ) .catch(err => { - console.error('Error when trying to update metdata on the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to update metdata on the MDC : ${err.message}\n`); }); } @@ -862,7 +862,7 @@ const editTeam = async (req, res) => { } ) .catch(err => { - console.error('Error when trying to update metdata on the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to update metdata on the MDC : ${err.message}\n`); }); } @@ -878,22 +878,22 @@ const editTeam = async (req, res) => { } ) .catch(err => { - console.error('Error when trying to update metdata on the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to update metdata on the MDC : ${err.message}\n`); }); } }) .catch(err => { - console.error('Error when trying to get the metdata from the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to get the metdata from the MDC : ${err.message}\n`); }); } }) .catch(err => { - console.error('Error when trying to login to MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to login to MDC : ${err.message}\n`); }); // 12. Log out of MDC await axios.post(metadataCatalogueLink + `/api/authentication/logout`, { withCredentials: true, timeout: 5000 }).catch(err => { - console.error('Error when trying to logout of the MDC - ' + err.message); + process.stdout.write(`TEAM - Error when trying to logout of the MDC : ${err.message}\n`); }); //13. Update datasets if name or member change @@ -919,7 +919,7 @@ const editTeam = async (req, res) => { return res.status(200).json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`TEAM - editTeam : ${err.message}\n`); return res.status(500).json(err.message); } }; diff --git a/src/resources/tool/data.repository.js b/src/resources/tool/data.repository.js index 4e47130d..5d423bfc 100644 --- a/src/resources/tool/data.repository.js +++ b/src/resources/tool/data.repository.js @@ -50,7 +50,7 @@ const addTool = async (req, res) => { data.journalYear = inputSanitizer.removeNonBreakingSpaces(journalYear); data.description = inputSanitizer.removeNonBreakingSpaces(description); data.resultsInsights = inputSanitizer.removeNonBreakingSpaces(resultsInsights); - console.log(req.body); + if (categories && typeof categories !== 'undefined') data.categories.category = inputSanitizer.removeNonBreakingSpaces(categories.category); data.license = inputSanitizer.removeNonBreakingSpaces(license); diff --git a/src/resources/tool/v1/tool.route.js b/src/resources/tool/v1/tool.route.js index 9b947348..fd23c95d 100644 --- a/src/resources/tool/v1/tool.route.js +++ b/src/resources/tool/v1/tool.route.js @@ -393,7 +393,7 @@ router.get('/:type/tag', passport.authenticate('jwt'), async (req, res) => { // 4. Return projects return res.status(200).json({ success: true, entities }); } catch (err) { - console.error(err.message); + process.stdout.write(`TOOL : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred searching for tools by tag', diff --git a/src/resources/tool/v2/tool.controller.js b/src/resources/tool/v2/tool.controller.js index 77554cef..b0cb4461 100644 --- a/src/resources/tool/v2/tool.controller.js +++ b/src/resources/tool/v2/tool.controller.js @@ -33,7 +33,7 @@ export default class ToolController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`TOOL - getTool : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', @@ -52,7 +52,7 @@ export default class ToolController extends Controller { }); } catch (err) { // Return error response if something goes wrong - console.error(err.message); + process.stdout.write(`TOOL - getTools : ${err.message}\n`); return res.status(500).json({ success: false, message: 'A server error occurred, please try again', diff --git a/src/resources/topic/topic.controller.js b/src/resources/topic/topic.controller.js index a3a40ab4..17b61d1f 100644 --- a/src/resources/topic/topic.controller.js +++ b/src/resources/topic/topic.controller.js @@ -6,12 +6,12 @@ module.exports = { buildRecipients: async (team, createdBy) => { // 1. Cause error if no members found if (_.isNull(team)) { - console.error('A topic cannot be created without a receiving team'); + process.stdout.write(`A topic cannot be created without a receiving team\n`); return []; } let { members } = team; if (_.isNull(members || members.length === 0)) { - console.error('A topic cannot be created with only the creating user'); + process.stdout.write(`A topic cannot be created with only the creating user\n`); return []; } let recipients = members.filter(mem => mem.roles.includes('manager') || mem.roles.includes('reviewer')).map(m => m.memberid); @@ -29,7 +29,7 @@ module.exports = { const { createdBy, relatedObjectIds } = context; // 1. Topic cannot be created without related object i.e. data/project/tool/paper if (_.isEmpty(relatedObjectIds)) { - console.error('No related object Id passed to build topic'); + process.stdout.write(`No related object Id passed to build topic\n`); return undefined; } // 2. Find the related object(s) in MongoDb and include team data @@ -39,7 +39,7 @@ module.exports = { .populate({ path: 'publisher', populate: { path: 'team' } }); // 3. Return undefined if no object exists if (_.isEmpty(tools)) { - console.error(`Failed to find related tool(s) with objectId(s): ${relatedObjectIds.join(', ')}`); + process.stdout.write(`Failed to find related tool(s) with objectId(s): ${relatedObjectIds.join(', ')}\n`); return undefined; } // 4. Iterate through each tool @@ -68,17 +68,17 @@ module.exports = { // 7. Get recipients for topic/message using the first tool (same team exists as each publisher is the same) let { publisher = '' } = tools[0]; if (_.isEmpty(publisher)) { - console.error(`No publisher associated to this dataset`); + process.stdout.write(`No publisher associated to this dataset\n`); return undefined; } let { team = [] } = publisher; if (_.isEmpty(team)) { - console.error(`No team associated to publisher, cannot message`); + process.stdout.write(`No team associated to publisher, cannot message\n`); return undefined; } const recipients = await module.exports.buildRecipients(team, createdBy); if (_.isEmpty(recipients)) { - console.error('A topic cannot be created without recipients'); + process.stdout.write(`A topic cannot be created without recipients\n`); return undefined; } // Future extension could be to iterate through tools at this point to generate a topic for each publisher @@ -98,7 +98,7 @@ module.exports = { // 9. Return created object return topic; } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - buildTopic : ${err.message}\n`); return undefined; } }, @@ -123,7 +123,7 @@ module.exports = { return topic; } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - findTopic : ${err.message}\n`); return undefined; } }, @@ -137,7 +137,7 @@ module.exports = { return res.status(201).json({ success: true, topic }); } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - createTopic : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -149,7 +149,7 @@ module.exports = { TopicModel.findByIdAndUpdate(id, { isDeleted: true, status: 'closed', expiryDate: Date.now() }, { new: true }); return res.status(204).json({ success: true }); } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - deleteTopic : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -186,7 +186,7 @@ module.exports = { ); return res.status(200).json({ success: true, topics }); } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - getTopics : ${err.message}\n`); return res.status(500).json(err.message); } }, @@ -211,7 +211,7 @@ module.exports = { // 5. Return original topic so unread messages are displayed correctly return res.status(200).json({ success: true, topic: dispatchTopic }); } catch (err) { - console.error(err.message); + process.stdout.write(`TOPIC - getTopicById : ${err.message}\n`); return res.status(500).json(err.message); } }, diff --git a/src/resources/user/user.register.route.js b/src/resources/user/user.register.route.js index 75f31c8e..0b883322 100644 --- a/src/resources/user/user.register.route.js +++ b/src/resources/user/user.register.route.js @@ -105,7 +105,7 @@ router.post('/', async (req, res) => { const [loginErr, token] = await to(login(req, user)); if (loginErr) { - console.error(loginErr); + process.stdout.write(`Authentication error\n`); return res.status(500).json({ success: false, data: 'Authentication error!' }); } diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 316c3113..82734491 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -141,7 +141,7 @@ router.patch('/advancedSearch/roles/:id', passport.authenticate('jwt'), utils.ch // serviceAccount // }); // } catch (err) { -// console.error(err.message); +// process.stdout.write(`USER - create service account: ${err.message}\n`); // return res.status(500).json(err); // } // }); diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index cfd68d19..75b1753c 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -4,12 +4,10 @@ import moment from 'moment'; import { UserModel } from '../user/user.model'; import helper from '../utilities/helper.util'; import constants from '../utilities/constants.util'; -import * as Sentry from '@sentry/node'; import wordTemplateBuilder from '../utilities/wordTemplateBuilder.util'; const fs = require('fs'); const nodemailer = require('nodemailer'); -const readEnv = process.env.ENV || 'production'; let parent, qsId; let questionList = []; @@ -2574,18 +2572,12 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta try { await transporter.sendMail(message, (error, info) => { if (error) { - return console.log(error); + return process.stdout.write(`sendMail : ${error.message}`); } - console.log('Email sent: ' + info.response); + process.stdout.write(`Email sent: ${info.response}`); }); } catch (error) { - console.error(error.response.body); - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(error); + process.stdout.write(`EMAIL GENERATOR - _sendEmail : ${error.message}\n`); } } }; @@ -2594,18 +2586,12 @@ const _sendEmailSmtp = async message => { try { await transporter.sendMail(message, (error, info) => { if (error) { - return console.log(error); + return process.stdout.write(`${error.message}\n`);; } - console.log('Email sent: ' + info.response); + process.stdout.write(`Email sent: ${info.response}`); }); } catch (error) { - console.error(error.response.body); - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(error); + process.stdout.write(`EMAIL GENERATOR - _sendEmailSmtp : ${error.message}\n`); } }; diff --git a/src/resources/utilities/logger.js b/src/resources/utilities/logger.js index 3b937197..39aab2b0 100644 --- a/src/resources/utilities/logger.js +++ b/src/resources/utilities/logger.js @@ -1,8 +1,5 @@ -import * as Sentry from '@sentry/node'; import constants from './constants.util'; -const readEnv = process.env.ENV || 'prod'; - const logRequestMiddleware = options => { return (req, res, next) => { const { logCategory, action } = options; @@ -13,26 +10,13 @@ const logRequestMiddleware = options => { const logSystemActivity = options => { const { category = 'Action not categorised', action = 'Action not described' } = options; - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category, - message: action, - level: Sentry.Severity.Info, - }); - } + process.stdout.write(`logSystemActivity - action: ${action}`); // Save to database }; const logUserActivity = (user, category, type, context) => { const { action } = context; - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category, - message: action, - level: Sentry.Severity.Info, - }); - } - console.log(`${action}`); + process.stdout.write(`logUserActivity - action: ${action}`); // Log date/time // Log action // Log if user was logged in @@ -41,14 +25,7 @@ const logUserActivity = (user, category, type, context) => { }; const logError = (err, category) => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err, { - tags: { - area: category, - }, - }); - } - console.error(`The following error occurred: ${err.message}`); + process.stdout.write(`The following error occurred: ${err.message}`); }; export const logger = { diff --git a/src/resources/utilities/notificationBuilder.js b/src/resources/utilities/notificationBuilder.js index 5bb081ad..ba68ea37 100644 --- a/src/resources/utilities/notificationBuilder.js +++ b/src/resources/utilities/notificationBuilder.js @@ -17,7 +17,7 @@ const triggerNotificationMessage = (messageRecipients, messageDescription, messa }); await message.save(async err => { if (err) { - console.error(`Failed to save ${messageType} message with error : ${err.message}`); + process.stdout.write(`NOTIFICATION BUILDER - Failed to save ${messageType} message with error : ${err.message}\n`); } }); }); diff --git a/src/resources/workflow/workflow.controller.js b/src/resources/workflow/workflow.controller.js index a48b6b7c..16bbcb6f 100644 --- a/src/resources/workflow/workflow.controller.js +++ b/src/resources/workflow/workflow.controller.js @@ -77,7 +77,7 @@ export default class WorkflowController extends Controller { }, }); } catch (err) { - console.error(err.message); + process.stdout.write(`WORKFLOW - getWorkflowById : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred searching for the specified workflow', @@ -158,7 +158,7 @@ export default class WorkflowController extends Controller { workflow: detailedWorkflow, }); } catch (err) { - console.error(err.message); + process.stdout.write(`WORKFLOW - createWorkflow : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred creating the workflow', @@ -259,7 +259,7 @@ export default class WorkflowController extends Controller { }); } } catch (err) { - console.error(err.message); + process.stdout.write(`WORKFLOW - updateWorkflow : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred editing the workflow', @@ -312,7 +312,7 @@ export default class WorkflowController extends Controller { // 5. Delete workflow WorkflowModel.deleteOne({ _id: workflowId }, function (err) { if (err) { - console.error(err.message); + process.stdout.write(`WORKFLOW - deleteOne : ${err.message}\n`); return res.status(400).json({ success: false, message: 'An error occurred deleting the workflow', @@ -345,7 +345,7 @@ export default class WorkflowController extends Controller { success: true, }); } catch (err) { - console.error(err.message); + process.stdout.write(`WORKFLOW - deleteWorkflow : ${err.message}\n`); return res.status(500).json({ success: false, message: 'An error occurred deleting the workflow', diff --git a/src/services/cachePubSub/cachePubSubClient.js b/src/services/cachePubSub/cachePubSubClient.js index 215b7c1e..246c4d48 100644 --- a/src/services/cachePubSub/cachePubSubClient.js +++ b/src/services/cachePubSub/cachePubSubClient.js @@ -11,14 +11,14 @@ export const publishMessageToChannel = async (channel, message) => { await client.connect(); } - client.on("connect", () => console.log("Redis cache is ready")); - client.on("error", (err) => console.log('Redis Client Error', err)); - client.on('ready', () => console.log('redis is running')); + client.on("connect", () => process.stdout.write(`Redis cache is ready`)); + client.on("error", (err) => process.stdout.write(`Redis Client Error : ${err.message}`)); + client.on('ready', () => process.stdout.write(`redis is running`)); await client.publish(channel, message); } catch (e) { - console.log(e); + process.stdout.write(`Redis Create Client Error : ${e.message}`); throw new Error(e.message); } } \ No newline at end of file diff --git a/src/services/google/PubSubService.js b/src/services/google/PubSubService.js index 9cd4789f..f47042c5 100644 --- a/src/services/google/PubSubService.js +++ b/src/services/google/PubSubService.js @@ -15,9 +15,9 @@ export const publishMessageToPubSub = async (topicName, message) => { try { const messageId = pubSubClient.topic(topicName).publishMessage({data: dataBuffer}); - console.log(`Message ${messageId} published.`); + process.stdout.write(`Message ${messageId} published.`); } catch (error) { - console.error(error); + process.stdout.write(`publishMessageToPubSub : ${error.message}`); throw new Error(`Received error while publishing a message to PubSub`); } }; \ No newline at end of file diff --git a/src/services/google/PubSubWithRetryService.js b/src/services/google/PubSubWithRetryService.js index da06ddc7..d18dca05 100644 --- a/src/services/google/PubSubWithRetryService.js +++ b/src/services/google/PubSubWithRetryService.js @@ -74,5 +74,5 @@ export const publishMessageWithRetryToPubSub = async (topicName, message) => { const [response] = await publisherClient.publish(request, { retry: retrySettings, }); - console.log(`Message ${response.messageIds} published.`); + process.stdout.write(`Message ${response.messageIds} published.`); } \ No newline at end of file diff --git a/src/services/httpClient/httpClient.js b/src/services/httpClient/httpClient.js index c18ebb0d..ea69afc8 100644 --- a/src/services/httpClient/httpClient.js +++ b/src/services/httpClient/httpClient.js @@ -24,7 +24,7 @@ class HttpClient { return response; } catch (err) { - console.error(err); + process.stdout.write(`HTTPCLIENT - POST : ${err.message}\n`); throw new Error(err.message); } } @@ -44,7 +44,7 @@ class HttpClient { return response; } catch (err) { - console.error(err); + process.stdout.write(`HTTPCLIENT - PUT : ${err.message}\n`); throw new Error(err.message); } } @@ -64,7 +64,7 @@ class HttpClient { return response; } catch (err) { - console.error(err); + process.stdout.write(`HTTPCLIENT - DELETE : ${err.message}\n`); throw new Error(err.message); } } diff --git a/src/services/hubspot/hubspot.js b/src/services/hubspot/hubspot.js index d1709e95..c4054b2d 100644 --- a/src/services/hubspot/hubspot.js +++ b/src/services/hubspot/hubspot.js @@ -1,5 +1,4 @@ import { Client, NumberOfRetries } from '@hubspot/api-client'; -import * as Sentry from '@sentry/node'; import { isEmpty, get, isNil, isNull } from 'lodash'; import { UserModel } from '../../resources/user/user.model'; @@ -10,7 +9,6 @@ import { logger } from '../../resources/utilities/logger'; // Default service params const apiKey = process.env.HUBSPOT_API_KEY; const logCategory = 'Hubspot Integration'; -const readEnv = process.env.ENV || 'prod'; let hubspotClient; if (apiKey) hubspotClient = new Client({ apiKey, numberOfApiCallRetries: NumberOfRetries.Three }); @@ -140,15 +138,6 @@ const createContact = async gatewayUser => { const syncAllContacts = async () => { if (apiKey) { try { - // Track attempted sync in Sentry using log - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'Hubspot', - message: `Syncing Gateway users with Hubspot contacts`, - level: Sentry.Severity.Log, - }); - } - // Batch import subscription changes from Hubspot await batchImportFromHubspot(); diff --git a/src/services/hubspot/hubspot.route.js b/src/services/hubspot/hubspot.route.js index ef9f92c2..4542c118 100644 --- a/src/services/hubspot/hubspot.route.js +++ b/src/services/hubspot/hubspot.route.js @@ -1,8 +1,6 @@ import express from 'express'; -import * as Sentry from '@sentry/node'; import hubspotConnector from './hubspot'; const router = express.Router(); -const readEnv = process.env.ENV || 'prod'; // @router POST /api/v1/hubspot/sync // @desc Performs a two-way sync of contact details including communication opt in preferences between HubSpot and the Gateway database @@ -29,10 +27,7 @@ router.post('/sync', async (req, res) => { // Return response indicating job has started (do not await async import) return res.status(200).json({ success: true, message: 'Sync started' }); } catch (err) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(err.message); + process.stdout.write(`HUBSPOT - SYNC : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Sync failed' }); } }); diff --git a/src/services/mailchimp/mailchimp.js b/src/services/mailchimp/mailchimp.js index cc5b1d5b..13d722f5 100644 --- a/src/services/mailchimp/mailchimp.js +++ b/src/services/mailchimp/mailchimp.js @@ -1,5 +1,4 @@ import Mailchimp from 'mailchimp-api-v3'; -import * as Sentry from '@sentry/node'; import Crypto from 'crypto'; import constants from '../../resources/utilities/constants.util'; import { UserModel } from '../../resources/user/user.model'; @@ -13,7 +12,6 @@ let mailchimp; if (apiKey) mailchimp = new Mailchimp(apiKey); const tags = ['Gateway User']; const defaultSubscriptionStatus = constants.mailchimpSubscriptionStatuses.SUBSCRIBED; -const readEnv = process.env.ENV || 'prod'; /** * Create MailChimp Subscription Subscriber @@ -37,21 +35,10 @@ const addSubscriptionMember = async (subscriptionId, user, status) => { LNAME: lastname, }, }; - // 2. Track attempted update in Sentry using log - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'MailChimp', - message: `Adding subscription for user: ${id} to subscription: ${subscriptionId}`, - level: Sentry.Severity.Log, - }); - } // 3. POST to MailChimp Marketing API to add the Gateway user to the MailChimp subscription members const md5email = Crypto.createHash('md5').update(email).digest('hex'); await mailchimp.put(`lists/${subscriptionId}/members/${md5email}`, body).catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(`Message: ${err.message} Errors: ${JSON.stringify(err.errors)}`); + process.stdout.write(`MAILCHIP - addSubscriptionMember : ${err.message}\n`); }); } }; @@ -104,22 +91,10 @@ const updateSubscriptionMembers = async (subscriptionId, members) => { skip_duplicate_check: true, update_existing: true, }; - // 4. Track attempted updates in Sentry using log - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'MailChimp', - message: `Updating subscribed for members: ${members.map( - member => `${member.userId} to ${member.status}` - )} against subscription: ${subscriptionId}`, - level: Sentry.Severity.Log, - }); - } + // 5. POST to MailChimp Marketing API to update member statuses await mailchimp.post(`lists/${subscriptionId}`, body).catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(`Message: ${err.message} Errors: ${JSON.stringify(err.errors)}`); + process.stdout.write(`MAILCHIP - updateSubscriptionMembers : ${err.message}\n`); }); } } @@ -134,22 +109,11 @@ const updateSubscriptionMembers = async (subscriptionId, members) => { */ const syncSubscriptionMembers = async subscriptionId => { if (apiKey) { - // 1. Track attempted sync in Sentry using log - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.addBreadcrumb({ - category: 'MailChimp', - message: `Syncing users for subscription: ${subscriptionId}`, - level: Sentry.Severity.Log, - }); - } // 2. Get total member count to anticipate chunking required to process all contacts const { stats: { member_count: subscribedCount, unsubscribe_count: unsubscribedCount }, } = await mailchimp.get(`lists/${subscriptionId}?fields=stats.member_count,stats.unsubscribe_count`).catch(err => { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(`Message: ${err.message} Errors: ${JSON.stringify(err.errors)}`); + process.stdout.write(`MAILCHIP - syncSubscriptionMembers : ${err.message}\n`); }); const memberCount = subscribedCount + unsubscribedCount; // 3. Batch update database to sync MailChimp to reflect users unsubscribed/subscribed externally diff --git a/src/services/mailchimp/mailchimp.route.js b/src/services/mailchimp/mailchimp.route.js index 7d3e451e..f1b521c2 100644 --- a/src/services/mailchimp/mailchimp.route.js +++ b/src/services/mailchimp/mailchimp.route.js @@ -1,8 +1,6 @@ import express from 'express'; -import * as Sentry from '@sentry/node'; import mailchimpConnector from './mailchimp'; const router = express.Router(); -const readEnv = process.env.ENV || 'prod'; // @router GET /api/v1/mailchimp/:subscriptionId/sync // @desc Performs a two-way sync of opt in preferences between MailChimp and the Gateway database @@ -32,10 +30,7 @@ router.post('/sync', async (req, res) => { // Return response indicating job has started (do not await async import) return res.status(200).json({ success: true, message: 'Sync started' }); } catch (err) { - if (readEnv === 'test' || readEnv === 'prod') { - Sentry.captureException(err); - } - console.error(err.message); + process.stdout.write(`MAILCHIP - sync : ${err.message}\n`); return res.status(500).json({ success: false, message: 'Sync failed' }); } }); diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 01d676ed..139e79c7 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -77,7 +77,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { return { authorised, userType }; } catch (error) { - console.error(error); + process.stdout.write(`DATASETONBOARDING - getUserPermissionsForDataset : ${error.message}\n`); return { authorised: false, userType: '' }; } }; @@ -335,7 +335,7 @@ const updateDataset = async (dataset, updateObj) => { // 2. If application is in progress, update initial question answers if (activeflag === constants.datasetStatuses.DRAFT || activeflag === constants.applicationStatuses.INREVIEW) { await Data.findByIdAndUpdate(_id, updateObj, { new: true }).catch(err => { - console.error(err); + process.stdout.write(`DATASETONBOARDING - Error updateDataset\n`); throw err; }); return dataset; From 22e03303312c71f4735fa9e437aeefbd6a02ba4b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 26 Oct 2022 13:10:07 +0100 Subject: [PATCH 004/229] update based on lgtm --- src/config/server.js | 1 - src/resources/utilities/logger.js | 2 +- src/services/mailchimp/mailchimp.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/config/server.js b/src/config/server.js index cadb2908..ac471da1 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -10,7 +10,6 @@ import cookieParser from 'cookie-parser'; import bodyParser from 'body-parser'; import { connectToDatabase } from './db'; import { initialiseAuthentication } from '../resources/auth'; -import helper from '../resources/utilities/helper.util'; require('dotenv').config(); diff --git a/src/resources/utilities/logger.js b/src/resources/utilities/logger.js index 39aab2b0..0be172e2 100644 --- a/src/resources/utilities/logger.js +++ b/src/resources/utilities/logger.js @@ -10,7 +10,7 @@ const logRequestMiddleware = options => { const logSystemActivity = options => { const { category = 'Action not categorised', action = 'Action not described' } = options; - process.stdout.write(`logSystemActivity - action: ${action}`); + process.stdout.write(`logSystemActivity : action ${action}, category ${category}`); // Save to database }; diff --git a/src/services/mailchimp/mailchimp.js b/src/services/mailchimp/mailchimp.js index 13d722f5..a342ab03 100644 --- a/src/services/mailchimp/mailchimp.js +++ b/src/services/mailchimp/mailchimp.js @@ -38,7 +38,7 @@ const addSubscriptionMember = async (subscriptionId, user, status) => { // 3. POST to MailChimp Marketing API to add the Gateway user to the MailChimp subscription members const md5email = Crypto.createHash('md5').update(email).digest('hex'); await mailchimp.put(`lists/${subscriptionId}/members/${md5email}`, body).catch(err => { - process.stdout.write(`MAILCHIP - addSubscriptionMember : ${err.message}\n`); + process.stdout.write(`MAILCHIP - addSubscriptionMember : ${id} - ${err.message}\n`); }); } }; From 7bea3176747b3b50bd002f6b9487e46a861ca704 Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 22 Nov 2022 14:32:53 +0000 Subject: [PATCH 005/229] fixes migrations required for adding new field automatically in a controlled fashion for GAT-1771 update --- .../1620418612003-test_migration.js | 0 .../1620558117918-applications_versioning.js | 0 .../1622731580031-first_message_is5safes.js | 0 .../1623235509532-authors_uploaders.js | 0 .../1623322905323-5safes_nhsd_removal.js | 0 .../1627566998386-add_globals.js | 0 ...68706696-shared_applications_versioning.js | 0 ...1621029553-update_publishers_uses5safes.js | 0 .../1631633422670-message_user_types.js | 0 ...525344331-Ig_2354_replace_hubs_with_hub.js | 0 ...-remove_projects_from_related_resources.js | 0 .../211122160600-add-new-flag-to-dar.js | 34 ++++++++++++++ .old.migrations/README.md | 40 ++++++++++++++++ {migrations => .old.migrations}/migrate.js | 2 +- google_analytics.json | 3 ++ migrate-mongo-config.js | 46 +++++++++++++++++++ ...337-add_published_flag_to_data_requests.js | 30 ++++++++++++ migrations/README.md | 46 ++++++++++++++----- package.json | 3 +- .../datarequest/datarequest.model.js | 1 + 20 files changed, 191 insertions(+), 14 deletions(-) rename {migrations => .old.migrations}/1620418612003-test_migration.js (100%) rename {migrations => .old.migrations}/1620558117918-applications_versioning.js (100%) rename {migrations => .old.migrations}/1622731580031-first_message_is5safes.js (100%) rename {migrations => .old.migrations}/1623235509532-authors_uploaders.js (100%) rename {migrations => .old.migrations}/1623322905323-5safes_nhsd_removal.js (100%) rename {migrations => .old.migrations}/1627566998386-add_globals.js (100%) rename {migrations => .old.migrations}/1631268706696-shared_applications_versioning.js (100%) rename {migrations => .old.migrations}/1631621029553-update_publishers_uses5safes.js (100%) rename {migrations => .old.migrations}/1631633422670-message_user_types.js (100%) rename {migrations => .old.migrations}/1633525344331-Ig_2354_replace_hubs_with_hub.js (100%) rename {migrations => .old.migrations}/1638716002879-remove_projects_from_related_resources.js (100%) create mode 100644 .old.migrations/211122160600-add-new-flag-to-dar.js create mode 100644 .old.migrations/README.md rename {migrations => .old.migrations}/migrate.js (65%) create mode 100644 google_analytics.json create mode 100644 migrate-mongo-config.js create mode 100644 migrations/20221122095337-add_published_flag_to_data_requests.js diff --git a/migrations/1620418612003-test_migration.js b/.old.migrations/1620418612003-test_migration.js similarity index 100% rename from migrations/1620418612003-test_migration.js rename to .old.migrations/1620418612003-test_migration.js diff --git a/migrations/1620558117918-applications_versioning.js b/.old.migrations/1620558117918-applications_versioning.js similarity index 100% rename from migrations/1620558117918-applications_versioning.js rename to .old.migrations/1620558117918-applications_versioning.js diff --git a/migrations/1622731580031-first_message_is5safes.js b/.old.migrations/1622731580031-first_message_is5safes.js similarity index 100% rename from migrations/1622731580031-first_message_is5safes.js rename to .old.migrations/1622731580031-first_message_is5safes.js diff --git a/migrations/1623235509532-authors_uploaders.js b/.old.migrations/1623235509532-authors_uploaders.js similarity index 100% rename from migrations/1623235509532-authors_uploaders.js rename to .old.migrations/1623235509532-authors_uploaders.js diff --git a/migrations/1623322905323-5safes_nhsd_removal.js b/.old.migrations/1623322905323-5safes_nhsd_removal.js similarity index 100% rename from migrations/1623322905323-5safes_nhsd_removal.js rename to .old.migrations/1623322905323-5safes_nhsd_removal.js diff --git a/migrations/1627566998386-add_globals.js b/.old.migrations/1627566998386-add_globals.js similarity index 100% rename from migrations/1627566998386-add_globals.js rename to .old.migrations/1627566998386-add_globals.js diff --git a/migrations/1631268706696-shared_applications_versioning.js b/.old.migrations/1631268706696-shared_applications_versioning.js similarity index 100% rename from migrations/1631268706696-shared_applications_versioning.js rename to .old.migrations/1631268706696-shared_applications_versioning.js diff --git a/migrations/1631621029553-update_publishers_uses5safes.js b/.old.migrations/1631621029553-update_publishers_uses5safes.js similarity index 100% rename from migrations/1631621029553-update_publishers_uses5safes.js rename to .old.migrations/1631621029553-update_publishers_uses5safes.js diff --git a/migrations/1631633422670-message_user_types.js b/.old.migrations/1631633422670-message_user_types.js similarity index 100% rename from migrations/1631633422670-message_user_types.js rename to .old.migrations/1631633422670-message_user_types.js diff --git a/migrations/1633525344331-Ig_2354_replace_hubs_with_hub.js b/.old.migrations/1633525344331-Ig_2354_replace_hubs_with_hub.js similarity index 100% rename from migrations/1633525344331-Ig_2354_replace_hubs_with_hub.js rename to .old.migrations/1633525344331-Ig_2354_replace_hubs_with_hub.js diff --git a/migrations/1638716002879-remove_projects_from_related_resources.js b/.old.migrations/1638716002879-remove_projects_from_related_resources.js similarity index 100% rename from migrations/1638716002879-remove_projects_from_related_resources.js rename to .old.migrations/1638716002879-remove_projects_from_related_resources.js diff --git a/.old.migrations/211122160600-add-new-flag-to-dar.js b/.old.migrations/211122160600-add-new-flag-to-dar.js new file mode 100644 index 00000000..4715ded1 --- /dev/null +++ b/.old.migrations/211122160600-add-new-flag-to-dar.js @@ -0,0 +1,34 @@ +import { DataRequest } from '../src/resources/datarequest/datarequest.model'; + +/** + * Make any changes you need to make to the database here + */ +async function up() { + // Write migration here + + /** + * Update DAR to include an overriding published field to determine the published + * state of a DAR edit form publication by a custodian + */ + await DataRequest.updateMany( + { + $set: { "data_requests.published_form": false } + } + ); + +} + +/** + * Make any changes that UNDO the up function side effects here (if possible) + */ +async function down() { + // Write migration here + + await DataRequest.updateMany( + { + $unset: { "data_requests.published_form": false } + } + ); +} + +module.exports = { up, down }; diff --git a/.old.migrations/README.md b/.old.migrations/README.md new file mode 100644 index 00000000..ce9cf4bb --- /dev/null +++ b/.old.migrations/README.md @@ -0,0 +1,40 @@ +# HDR UK GATEWAY - Data Migrations + +The primary data source used by the Gateway Project is the noSQL solution provided by MongoDb. Data migration strategy is a fundamental part of software development and release cycles for a data intensive web application. The project team have chosen the NPM package Migrate-Mongoose - https://www.npmjs.com/package/migrate-mongoose to assist in the management of data migration scripts. This package allows developers to write versioned, reversible data migration scripts using the Mongoose library. + +For more information on what migration scripts are and their purpose, please see sample background reading here - https://www.red-gate.com/simple-talk/sql/database-administration/using-migration-scripts-in-database-deployments/ + +### Using migration scrips + +To create a data migration script, follow these steps: + +#### Step 1 + +Ensure your terminal's working directory is the Gateway API and that node packages have been installed using 'npm i'. + +#### Step 2 + +Run the command below, replacing 'my_new_migration_script' with the name of the script you want to create. The name does not need to be unique, as it will be prefixed automatically with a timestamp, but it should be easily recognisable and relate strongly to the database change that will take place if the script is executed. + +./node_modules/.bin/migrate create my_new_migration_script + +#### Step 3 + +Your new migration scripts should now be available in './migrations/', which you can now modify. You can import the required Mongoose models as normal to interact with the MongoDb database. The migration scripts that run locally will use the connection string taken from your .env file against the variable 'MIGRATE_dbConnectionUri'. + +Complete the scripts required for the UP process, and if possible, the DOWN process. For awareness, the UP scripts run automatically as part of our CI/CD pipeline, and the DOWN scripts exist to reverse database changes if necessary, this is a manual process. + +#### Step 4 + +With the scripts written, the functions can be tested by running the following command, replacing 'my_new_migration_script' with the name of the script you want to execute without the time stamp so for example +node -r esm migrations/migrate.js up add_globals + +node -r esm migrations/migrate.js up my_new_migration_script + +When this process is completed, the connected database will have a new document representing your migration scripts inside the 'migrations' collection, which tracks the state of the migration. If you need to run your scripts multiple times for test purposes, you can change the state of the migration to 'Down'. + +During this process, please ensure you are using a personal database. + +#### Step 5 + +Commit the code to the relevant git branch and raise a pull request. The migration script will run automatically as the code moves through each environment. diff --git a/migrations/migrate.js b/.old.migrations/migrate.js similarity index 65% rename from migrations/migrate.js rename to .old.migrations/migrate.js index 7226f512..43130a3b 100644 --- a/migrations/migrate.js +++ b/.old.migrations/migrate.js @@ -1,7 +1,7 @@ import cli from 'migrate-mongoose/src/cli'; //lgtm [js/unused-local-variable] import mongoose from 'mongoose'; -mongoose.connect(process.env.MIGRATE_dbConnectionUri, { +mongoose.connect(`${process.env.MIGRATE_dbConnectionUri}/${process.env.database}/?retryWrites=true&w=majority`, { useNewUrlParser: true, useFindAndModify: false, useUnifiedTopology: true, diff --git a/google_analytics.json b/google_analytics.json new file mode 100644 index 00000000..544b7b4d --- /dev/null +++ b/google_analytics.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/migrate-mongo-config.js b/migrate-mongo-config.js new file mode 100644 index 00000000..419d12bf --- /dev/null +++ b/migrate-mongo-config.js @@ -0,0 +1,46 @@ +// In this file you can configure migrate-mongo + +// Have to call this as this is pre-app start, thus env hasn't +// been populated yet +require('dotenv').config(); + +const config = { + mongodb: { + // TODO Change (or review) the url to your MongoDB: + url: 'mongodb+srv://' + + process.env.user + + ':' + + process.env.password + + '@' + + process.env.cluster + + '?ssl=true&retryWrites=true&w=majority', + + // TODO Change this to your database name: + databaseName: process.env.database, + + options: { + useNewUrlParser: true, // removes a deprecation warning when connecting + useUnifiedTopology: true, // removes a deprecating warning when connecting + // connectTimeoutMS: 3600000, // increase connection timeout to 1 hour + // socketTimeoutMS: 3600000, // increase socket timeout to 1 hour + } + }, + + // The migrations dir, can be an relative or absolute path. Only edit this when really necessary. + migrationsDir: "migrations", + + // The mongodb collection where the applied changes are stored. Only edit this when really necessary. + changelogCollectionName: "changelog", + + // The file extension to create migrations and search for in migration dir + migrationFileExtension: ".js", + + // Enable the algorithm to create a checksum of the file contents and use that in the comparison to determine + // if the file should be run. Requires that scripts are coded to be run multiple times. + useFileHash: false, + + // Don't change this, unless you know what you're doing + moduleSystem: 'commonjs', +}; + +module.exports = config; diff --git a/migrations/20221122095337-add_published_flag_to_data_requests.js b/migrations/20221122095337-add_published_flag_to_data_requests.js new file mode 100644 index 00000000..aabdc3f6 --- /dev/null +++ b/migrations/20221122095337-add_published_flag_to_data_requests.js @@ -0,0 +1,30 @@ +module.exports = { + async up(db, client) { + // TODO write your migration here. + // See https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script + + /** + * Update DAR to include an overriding published field to determine the published + * state of a DAR edit form publication by a custodian + */ + // await db.collection('data_requests').updateMany({ + // $set: { "published_form": false }, + // }); + + await db.collection('data_requests').updateMany({}, + { + $set: { "publishedForm": false } + } + ); + }, + + async down(db, client) { + // TODO write the statements to rollback your migration (if possible) + + await db.collection('data_requests').updateMany({}, + { + $unset: { "publishedForm": false } + } + ); + } +}; diff --git a/migrations/README.md b/migrations/README.md index ce9cf4bb..0cc32ab4 100644 --- a/migrations/README.md +++ b/migrations/README.md @@ -1,8 +1,13 @@ # HDR UK GATEWAY - Data Migrations -The primary data source used by the Gateway Project is the noSQL solution provided by MongoDb. Data migration strategy is a fundamental part of software development and release cycles for a data intensive web application. The project team have chosen the NPM package Migrate-Mongoose - https://www.npmjs.com/package/migrate-mongoose to assist in the management of data migration scripts. This package allows developers to write versioned, reversible data migration scripts using the Mongoose library. +The primary data source used by the Gateway Project is the noSQL solution provided by MongoDb. +Data migration strategy is a fundemental part of software development and release cycles for a +data intensive web application. The project team have chosen the NPM package Migrate-Mongo - https://www.npmjs.com/package/migrate-mongo +to assist in the management of data migration scripts. This package allows developers to write versioned, +reversible data migration scripts using the Mongoose library. -For more information on what migration scripts are and their purpose, please see sample background reading here - https://www.red-gate.com/simple-talk/sql/database-administration/using-migration-scripts-in-database-deployments/ +For more information on what migration scripts are and their purpose, please see sample +background reading here - https://www.red-gate.com/simple-talk/sql/database-administration/using-migration-scripts-in-database-deployments/ ### Using migration scrips @@ -10,31 +15,48 @@ To create a data migration script, follow these steps: #### Step 1 -Ensure your terminal's working directory is the Gateway API and that node packages have been installed using 'npm i'. +Ensure your terminal's working directory is the Gateway API and that node packages have +been installed using 'npm i'. #### Step 2 -Run the command below, replacing 'my_new_migration_script' with the name of the script you want to create. The name does not need to be unique, as it will be prefixed automatically with a timestamp, but it should be easily recognisable and relate strongly to the database change that will take place if the script is executed. +Run the command below, replacing 'my_new_migration_script' with the name of the script +you want to create. The name does not need to be unique, as it will be prefixed automatically +with a timestamp, but it should be easily recognisable and relate strongly to the database +change that will take place if the script is executed. -./node_modules/.bin/migrate create my_new_migration_script +./node_modules/.bin/migrate-mongo create my_new_migration_script #### Step 3 -Your new migration scripts should now be available in './migrations/', which you can now modify. You can import the required Mongoose models as normal to interact with the MongoDb database. The migration scripts that run locally will use the connection string taken from your .env file against the variable 'MIGRATE_dbConnectionUri'. +Your new migration scripts should now be available in './migrations/', which you can now modify. +You can interact directly with the database. The migration scripts that run locally will use the +connection string config taken from your .env file against the variables: database, user, password and cluster. -Complete the scripts required for the UP process, and if possible, the DOWN process. For awareness, the UP scripts run automatically as part of our CI/CD pipeline, and the DOWN scripts exist to reverse database changes if necessary, this is a manual process. +Complete the scripts required for the UP process, and if possible, the DOWN process. For awareness, the UP +scripts run automatically as part of our CI/CD pipeline, and the DOWN scripts exist to reverse +database changes if necessary, this is a manual process. #### Step 4 -With the scripts written, the functions can be tested by running the following command, replacing 'my_new_migration_script' with the name of the script you want to execute without the time stamp so for example -node -r esm migrations/migrate.js up add_globals +With the scripts written, the functions can be tested by running the following command, +replacing 'my_new_migration_script' with the name of the script you want to execute without +the time stamp so for example -node -r esm migrations/migrate.js up my_new_migration_script +./node_modules/.bin/migrate-mongo up (to run all migration updates) +./node_modules/.bin/migrate-mongo down (to rollback migration updates) +./node_modules/.bin/migrate-mongo up my_new_migration_script (to run a single migration update) +./node_modules/.bin/migrate-mongo down my_new_migration_script (to rollback a single migration update) +./node_modules/.bin/migrate-mongo status (to list any pending migrations yet to be run) -When this process is completed, the connected database will have a new document representing your migration scripts inside the 'migrations' collection, which tracks the state of the migration. If you need to run your scripts multiple times for test purposes, you can change the state of the migration to 'Down'. +When this process is completed, the connected database will have a new document representing your +migration scripts inside the 'migrations' collection, which tracks the state of the migration. +If you need to run your scripts multiple times for test purposes, you can change the state of +the migration to 'Down'. During this process, please ensure you are using a personal database. #### Step 5 -Commit the code to the relevant git branch and raise a pull request. The migration script will run automatically as the code moves through each environment. +Commit the code to the relevant git branch and raise a pull request. The migration script +will run automatically as the code moves through each environment. diff --git a/package.json b/package.json index a3211fe6..294ee1fd 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "keygrip": "^1.1.0", "lodash": "^4.17.19", "mailchimp-api-v3": "^1.15.0", - "migrate-mongoose": "^4.0.0", + "migrate-mongo": "^9.0.0", "moment": "^2.29.3", "mongoose": "^5.12.7", "morgan": "^1.10.0", @@ -87,6 +87,7 @@ "supertest": "^4.0.2" }, "scripts": { + "start-with-migrate": "./node_modules/.bin/migrate up && node index.js", "start": "node index.js", "server": "nodemon --ignore 'src/**/*.json' index.js", "debug": "nodemon --inspect=0.0.0.0:3001 index.js", diff --git a/src/resources/datarequest/datarequest.model.js b/src/resources/datarequest/datarequest.model.js index 6a82bd2f..f1415257 100644 --- a/src/resources/datarequest/datarequest.model.js +++ b/src/resources/datarequest/datarequest.model.js @@ -103,6 +103,7 @@ const DataRequestSchema = new Schema( originId: { type: Schema.Types.ObjectId, ref: 'data_request' }, versionTree: { type: Object, default: {} }, isShared: { type: Boolean, default: false }, + publishedForm: { type: Boolean, default: false }, }, { timestamps: true, From 66133a1099fb695f9f90e2e5e32b06f2f93fd9f2 Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 22 Nov 2022 14:34:39 +0000 Subject: [PATCH 006/229] remove new migration from old non-working migration structure --- .../211122160600-add-new-flag-to-dar.js | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 .old.migrations/211122160600-add-new-flag-to-dar.js diff --git a/.old.migrations/211122160600-add-new-flag-to-dar.js b/.old.migrations/211122160600-add-new-flag-to-dar.js deleted file mode 100644 index 4715ded1..00000000 --- a/.old.migrations/211122160600-add-new-flag-to-dar.js +++ /dev/null @@ -1,34 +0,0 @@ -import { DataRequest } from '../src/resources/datarequest/datarequest.model'; - -/** - * Make any changes you need to make to the database here - */ -async function up() { - // Write migration here - - /** - * Update DAR to include an overriding published field to determine the published - * state of a DAR edit form publication by a custodian - */ - await DataRequest.updateMany( - { - $set: { "data_requests.published_form": false } - } - ); - -} - -/** - * Make any changes that UNDO the up function side effects here (if possible) - */ -async function down() { - // Write migration here - - await DataRequest.updateMany( - { - $unset: { "data_requests.published_form": false } - } - ); -} - -module.exports = { up, down }; From 75c137725127935b8b34100b38fa649e58e172b3 Mon Sep 17 00:00:00 2001 From: Loki Date: Thu, 24 Nov 2022 13:57:22 +0000 Subject: [PATCH 007/229] per comment --- .gitignore | 1 + migrations/README.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index f95121eb..9554a6c6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ npm-debug.log* package-lock.json .env globalConfig.json +google_analytics.json diff --git a/migrations/README.md b/migrations/README.md index 0cc32ab4..619c2029 100644 --- a/migrations/README.md +++ b/migrations/README.md @@ -60,3 +60,9 @@ During this process, please ensure you are using a personal database. Commit the code to the relevant git branch and raise a pull request. The migration script will run automatically as the code moves through each environment. + +#### Note + +You can avoid running migrations manually, you can use `npm run start-with-migrate` to launch the api +locally, with any pending migrations to be run - Ensure the targetted database is correct to avoid any +unwanted migrations elsewhere. From eb35daa3f5e11d3490a0def571fa37a271e13268 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 25 Nov 2022 15:46:38 +0000 Subject: [PATCH 008/229] tech debt reviews --- src/config/server.js | 5 +- .../{tool => review}/review.model.js | 0 .../{tool => review/v1}/review.route.js | 35 ++++----- src/resources/review/v3/review.controller.js | 77 +++++++++++++++++++ src/resources/review/v3/review.route.js | 18 +++++ src/resources/tool/v1/tool.route.js | 2 +- 6 files changed, 113 insertions(+), 24 deletions(-) rename src/resources/{tool => review}/review.model.js (100%) rename src/resources/{tool => review/v1}/review.route.js (69%) create mode 100644 src/resources/review/v3/review.controller.js create mode 100644 src/resources/review/v3/review.route.js diff --git a/src/config/server.js b/src/config/server.js index ac471da1..11a29f32 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -167,7 +167,10 @@ app.use('/api/v1/publishers', require('../resources/publisher/publisher.route')) app.use('/api/v1/teams', require('../resources/team/team.route')); app.use('/api/v1/workflows', require('../resources/workflow/workflow.route')); app.use('/api/v1/messages', require('../resources/message/message.route')); -app.use('/api/v1/reviews', require('../resources/tool/review.route')); + +app.use('/api/v1/reviews', require('../resources/review/v1/review.route')); +app.use('/api/v3/reviews', require('../resources/review/v3/review.route')); + app.use('/api/v1/relatedobject/', require('../resources/relatedobjects/relatedobjects.route')); app.use('/api/v1/accounts', require('../resources/account/account.route')); diff --git a/src/resources/tool/review.model.js b/src/resources/review/review.model.js similarity index 100% rename from src/resources/tool/review.model.js rename to src/resources/review/review.model.js diff --git a/src/resources/tool/review.route.js b/src/resources/review/v1/review.route.js similarity index 69% rename from src/resources/tool/review.route.js rename to src/resources/review/v1/review.route.js index a2a90878..352532cb 100644 --- a/src/resources/tool/review.route.js +++ b/src/resources/review/v1/review.route.js @@ -1,18 +1,15 @@ import express from 'express'; -import { ROLES } from '../user/user.roles'; -import { Reviews } from './review.model'; +import { ROLES } from '../../user/user.roles'; +import { Reviews } from '../review.model'; import passport from 'passport'; -import { utils } from '../auth'; -import helper from '../utilities/helper.util'; +import { utils } from '../../auth'; +import helper from '../../utilities/helper.util'; const router = express.Router(); -/** - * {get} /accountsearch Search tools - * - * Return list of tools, this can be with filters or/and search criteria. This will also include pagination on results. - * The free word search criteria can be improved on with node modules that specialize with searching i.e. js-search - */ +// @router GET /api/v1/reviews/admin/pending +// @desc get reviews in pending for user.role = Admin +// @access Public router.get('/admin/pending', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin), async (req, res) => { var r = Reviews.aggregate([ { $lookup: { from: 'tools', localField: 'reviewerID', foreignField: 'id', as: 'person' } }, @@ -28,12 +25,9 @@ router.get('/admin/pending', passport.authenticate('jwt'), utils.checkIsInRole(R }); }); -/** - * {get} /accountsearch Search tools - * - * Return list of tools, this can be with filters or/and search criteria. This will also include pagination on results. - * The free word search criteria can be improved on with node modules that specialize with searching i.e. js-search - */ +// @router GET /api/v1/reviews/pending +// @desc get reviews in pending for user.role = Creator by reviewerID +// @access Public router.get('/pending', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Creator), async (req, res) => { var idString = ''; @@ -59,12 +53,9 @@ router.get('/pending', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.C }); }); -/** - * {get} /accountsearch Search tools - * - * Return list of tools, this can be with filters or/and search criteria. This will also include pagination on results. - * The free word search criteria can be improved on with node modules that specialize with searching i.e. js-search - */ +// @router GET /api/v1/reviews +// @desc find reviews by reviewID +// @access Public router.get('/', async (req, res) => { var reviewIDString = ''; diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js new file mode 100644 index 00000000..fc819b47 --- /dev/null +++ b/src/resources/review/v3/review.controller.js @@ -0,0 +1,77 @@ +import { Reviews } from '../review.model'; +import helper from '../../utilities/helper.util'; + +class ReviewController { + constructor() {} + + async handleReviewsUsersPending(req, res) { + const userRole = req.params.role; + const idString = parseInt(req.query.id) || ''; + + let pipeline, statement, response; + let responseApi = {}; + + responseApi.success = true; + + pipeline = this.reviewDynamicPipeline(userRole, 'active', idString); + statement = Reviews.aggregate(pipeline); + response = await statement.exec(); + + if (userRole === 'admin') { + response.map(item => { + item.person = helper.hidePrivateProfileDetails(item.person); + }); + } + responseApi.data = response; + + if (userRole === 'creator') { + pipeline = this.reviewDynamicPipeline('active', 'active', idString); + statement = Reviews.aggregate(pipeline); + response = await statement.exec(); + responseApi.allReviews = response; + } + + return res.status(200).json(responseApi); + } + + async handleReviewsByReviewId (req, res) { + let reviewIDString = ''; + + if (req.query.id) { + reviewIDString = parseInt(req.query.id); + } + + try { + const r = Reviews.aggregate([ + { $match: { $and: [{ activeflag: 'active' }, { reviewID: reviewIDString }] } }, + { $lookup: { from: 'tools', localField: 'reviewerID', foreignField: 'id', as: 'person' } }, + { $lookup: { from: 'tools', localField: 'toolID', foreignField: 'id', as: 'tool' } }, + ]); + r.exec((err, data) => { + if (err) return res.json({ success: false, error: err }); + + data.map(dat => { + dat.person = helper.hidePrivateProfileDetails(dat.person); + }); + return res.json({ success: true, data: data }); + }); + } catch (err) { + process.stdout.write(`ReviewController.handleReviewsByReviewId : ${err.message}`); + throw new Error(`An error occurred : ${err.message}`); + } + } + + reviewDynamicPipeline(role, flag, idString) { + let query = []; + + if (role === 'creator') { + query.push({ $match: { $and: [{ activeflag: flag }, { reviewID: idString }] } }); + } + query.push({ $lookup: { from: 'tools', localField: 'reviewerID', foreignField: 'id', as: 'person' } }); + query.push({ $lookup: { from: 'tools', localField: 'toolID', foreignField: 'id', as: 'tool' } }); + + return query; + } +} + +module.exports = new ReviewController(); \ No newline at end of file diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js new file mode 100644 index 00000000..9fc29c65 --- /dev/null +++ b/src/resources/review/v3/review.route.js @@ -0,0 +1,18 @@ +import express from 'express'; +import passport from 'passport'; + +const ReviewController = require('./review.controller'); + +const router = express.Router(); + +// @router GET /api/v1/reviews/:role/pending +// @desc find reviews in pending based on users.role = Creator / Admin +// @access Private +router.get('/:role(creator|admin)/pending', passport.authenticate('jwt'), (req, res) => ReviewController.handleReviewsUsersPending(req, res)); + +// @router GET /api/v1/reviews +// @desc find reviews by reviewID +// @access Public +router.get('/', async (req, res) => ReviewController.handleReviewsByReviewId(req, res)); + +module.exports = router; diff --git a/src/resources/tool/v1/tool.route.js b/src/resources/tool/v1/tool.route.js index fd23c95d..d202c34e 100644 --- a/src/resources/tool/v1/tool.route.js +++ b/src/resources/tool/v1/tool.route.js @@ -1,7 +1,7 @@ /* eslint-disable no-undef */ import express from 'express'; import { ROLES } from '../../user/user.roles'; -import { Reviews } from '../review.model'; +import { Reviews } from '../../review/review.model'; import { Data } from '../data.model'; import { Course } from '../../course/course.model'; import { DataUseRegister } from '../../dataUseRegister/dataUseRegister.model'; From 65c18648407e565cf9e48a86535b0491ce7df077 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 25 Nov 2022 16:05:10 +0000 Subject: [PATCH 009/229] clean up --- src/resources/review/v3/review.controller.js | 26 ++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index fc819b47..dd5d20ad 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -8,27 +8,22 @@ class ReviewController { const userRole = req.params.role; const idString = parseInt(req.query.id) || ''; - let pipeline, statement, response; + let responseDB; let responseApi = {}; responseApi.success = true; - pipeline = this.reviewDynamicPipeline(userRole, 'active', idString); - statement = Reviews.aggregate(pipeline); - response = await statement.exec(); + responseDB = await this.statementExecution(userRole, 'active', idString); if (userRole === 'admin') { - response.map(item => { + responseDB.map(item => { item.person = helper.hidePrivateProfileDetails(item.person); }); } - responseApi.data = response; + responseApi.data = responseDB; if (userRole === 'creator') { - pipeline = this.reviewDynamicPipeline('active', 'active', idString); - statement = Reviews.aggregate(pipeline); - response = await statement.exec(); - responseApi.allReviews = response; + responseApi.allReviews = await this.statementExecution('active', 'active', idString); } return res.status(200).json(responseApi); @@ -61,6 +56,17 @@ class ReviewController { } } + async statementExecution(role, flag, idString) { + try { + const pipeline = this.reviewDynamicPipeline(role, flag, idString); + const statement = Reviews.aggregate(pipeline); + return await statement.exec(); + } catch (err) { + process.stdout.write(`ReviewController.handleReviewsByReviewId : ${err.message}`); + throw new Error(`An error occurred : ${err.message}`); + } + } + reviewDynamicPipeline(role, flag, idString) { let query = []; From 0d133b3fe3448cac0dd51854faa5fc58d411056e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 25 Nov 2022 16:09:04 +0000 Subject: [PATCH 010/229] update name --- src/resources/review/v3/review.controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index dd5d20ad..c91f6a8e 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -13,7 +13,7 @@ class ReviewController { responseApi.success = true; - responseDB = await this.statementExecution(userRole, 'active', idString); + responseDB = await this.statementExecutionDB(userRole, 'active', idString); if (userRole === 'admin') { responseDB.map(item => { @@ -23,7 +23,7 @@ class ReviewController { responseApi.data = responseDB; if (userRole === 'creator') { - responseApi.allReviews = await this.statementExecution('active', 'active', idString); + responseApi.allReviews = await this.statementExecutionDB('active', 'active', idString); } return res.status(200).json(responseApi); @@ -56,7 +56,7 @@ class ReviewController { } } - async statementExecution(role, flag, idString) { + async statementExecutionDB(role, flag, idString) { try { const pipeline = this.reviewDynamicPipeline(role, flag, idString); const statement = Reviews.aggregate(pipeline); From 4ebc27b4c2646d353fbb316ecd1c14d8dcff77df Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 28 Nov 2022 12:30:55 +0000 Subject: [PATCH 011/229] update reviews --- src/resources/review/v3/review.controller.js | 77 +++++--------------- src/resources/review/v3/review.route.js | 11 +-- 2 files changed, 22 insertions(+), 66 deletions(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index c91f6a8e..3e716c3b 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -1,83 +1,44 @@ import { Reviews } from '../review.model'; -import helper from '../../utilities/helper.util'; class ReviewController { constructor() {} - async handleReviewsUsersPending(req, res) { - const userRole = req.params.role; - const idString = parseInt(req.query.id) || ''; + async handleReviews(req, res) { + const idString = parseInt(req.params.reviewId) || ''; - let responseDB; - let responseApi = {}; + const data = await this.statementExecutionDB(idString); - responseApi.success = true; - - responseDB = await this.statementExecutionDB(userRole, 'active', idString); - - if (userRole === 'admin') { - responseDB.map(item => { - item.person = helper.hidePrivateProfileDetails(item.person); - }); - } - responseApi.data = responseDB; - - if (userRole === 'creator') { - responseApi.allReviews = await this.statementExecutionDB('active', 'active', idString); - } - - return res.status(200).json(responseApi); - } - - async handleReviewsByReviewId (req, res) { - let reviewIDString = ''; - - if (req.query.id) { - reviewIDString = parseInt(req.query.id); - } - - try { - const r = Reviews.aggregate([ - { $match: { $and: [{ activeflag: 'active' }, { reviewID: reviewIDString }] } }, - { $lookup: { from: 'tools', localField: 'reviewerID', foreignField: 'id', as: 'person' } }, - { $lookup: { from: 'tools', localField: 'toolID', foreignField: 'id', as: 'tool' } }, - ]); - r.exec((err, data) => { - if (err) return res.json({ success: false, error: err }); - - data.map(dat => { - dat.person = helper.hidePrivateProfileDetails(dat.person); - }); - return res.json({ success: true, data: data }); - }); - } catch (err) { - process.stdout.write(`ReviewController.handleReviewsByReviewId : ${err.message}`); - throw new Error(`An error occurred : ${err.message}`); - } + return res.status(200).json({ + 'success': true, + data + }); } - async statementExecutionDB(role, flag, idString) { + async statementExecutionDB(idString) { try { - const pipeline = this.reviewDynamicPipeline(role, flag, idString); + const pipeline = this.reviewDynamicPipeline(idString); const statement = Reviews.aggregate(pipeline); return await statement.exec(); } catch (err) { - process.stdout.write(`ReviewController.handleReviewsByReviewId : ${err.message}`); + process.stdout.write(`ReviewController.statementExecutionDB : ${err.message}`); throw new Error(`An error occurred : ${err.message}`); } } - reviewDynamicPipeline(role, flag, idString) { + reviewDynamicPipeline(idString) { let query = []; - if (role === 'creator') { - query.push({ $match: { $and: [{ activeflag: flag }, { reviewID: idString }] } }); + if (idString) { + query.push({ $match: { 'reviewID': idString } }); } - query.push({ $lookup: { from: 'tools', localField: 'reviewerID', foreignField: 'id', as: 'person' } }); - query.push({ $lookup: { from: 'tools', localField: 'toolID', foreignField: 'id', as: 'tool' } }); + query.push({ "$lookup": { "from": "tools", "localField": "reviewerID", "foreignField": "id", "as": "person" } }); + query.push({ "$lookup": { "from": "tools", "localField": "toolID", "foreignField": "id", "as": "tool" } }); + + console.log(JSON.stringify(query)); return query; } + } -module.exports = new ReviewController(); \ No newline at end of file +module.exports = new ReviewController(); diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index 9fc29c65..555ddb73 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -5,14 +5,9 @@ const ReviewController = require('./review.controller'); const router = express.Router(); -// @router GET /api/v1/reviews/:role/pending -// @desc find reviews in pending based on users.role = Creator / Admin +// @router GET /api/v1/reviews/:reviewId? +// @desc get all reviews or find reviews by reviewId // @access Private -router.get('/:role(creator|admin)/pending', passport.authenticate('jwt'), (req, res) => ReviewController.handleReviewsUsersPending(req, res)); - -// @router GET /api/v1/reviews -// @desc find reviews by reviewID -// @access Public -router.get('/', async (req, res) => ReviewController.handleReviewsByReviewId(req, res)); +router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewController.handleReviews(req, res)); module.exports = router; From 640ba2ef27fd5f68db196c945a1adc44ada1a649 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 28 Nov 2022 13:58:07 +0000 Subject: [PATCH 012/229] cosmetic changes --- src/resources/review/v3/review.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index 555ddb73..784f7693 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -5,7 +5,7 @@ const ReviewController = require('./review.controller'); const router = express.Router(); -// @router GET /api/v1/reviews/:reviewId? +// @router GET /api/v3/reviews/:reviewId? // @desc get all reviews or find reviews by reviewId // @access Private router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewController.handleReviews(req, res)); From 9a386efe73afe8bb772d9d08b5e4ddd103dc3188 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 28 Nov 2022 13:59:50 +0000 Subject: [PATCH 013/229] cosmetic changes --- src/resources/review/v3/review.controller.js | 2 +- src/resources/review/v3/review.route.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index 3e716c3b..d1c18121 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -3,7 +3,7 @@ import { Reviews } from '../review.model'; class ReviewController { constructor() {} - async handleReviews(req, res) { + async getReviews(req, res) { const idString = parseInt(req.params.reviewId) || ''; const data = await this.statementExecutionDB(idString); diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index 784f7693..e5a84a62 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -8,6 +8,6 @@ const router = express.Router(); // @router GET /api/v3/reviews/:reviewId? // @desc get all reviews or find reviews by reviewId // @access Private -router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewController.handleReviews(req, res)); +router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewController.getReviews(req, res)); module.exports = router; From 4458288535977c573465472cda0e4a91462de0df Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 28 Nov 2022 16:05:27 +0000 Subject: [PATCH 014/229] update code for GAT-1794 --- .../collections/collections.controller.js | 1 - src/resources/collections/collections.service.js | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/resources/collections/collections.controller.js b/src/resources/collections/collections.controller.js index 632cc4da..146b7622 100644 --- a/src/resources/collections/collections.controller.js +++ b/src/resources/collections/collections.controller.js @@ -76,7 +76,6 @@ export default class CollectionsController extends Controller { async getCollectionRelatedResources(req, res) { let collectionID = parseInt(req.params.collectionID); - try { const data = await this.collectionsService.getCollectionObjects(collectionID); return res.json({ success: true, data: data }); diff --git a/src/resources/collections/collections.service.js b/src/resources/collections/collections.service.js index d495a596..1c5c13ed 100644 --- a/src/resources/collections/collections.service.js +++ b/src/resources/collections/collections.service.js @@ -33,6 +33,7 @@ export default class CollectionsService { } else { for (let object of res[0].relatedObjects) { let relatedObject = await this.getCollectionObject(object.objectId, object.objectType, object.pid, object.updated); + if (!_.isUndefined(relatedObject)) { relatedObjects.push(relatedObject); } else { @@ -112,7 +113,18 @@ export default class CollectionsService { } ) .populate([ - { path: 'gatewayDatasetsInfo', select: { name: 1 } }, + { + path: 'gatewayDatasetsInfo', + match: { + activeflag: { + $eq: 'active' + } + }, + select: { + name: 1, + activeflag: 1 + } + }, { path: 'publisherInfo', select: { name: 1, _id: 0 }, From 5d1cd3252c9009071ed8a6659dd9d1e088bba9a6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 11:48:43 +0000 Subject: [PATCH 015/229] review patch tech debt --- src/resources/review/v3/review.controller.js | 52 ++++++++++++++++---- src/resources/review/v3/review.route.js | 7 +++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index d1c18121..3b69fb43 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -1,12 +1,14 @@ import { Reviews } from '../review.model'; +import { storeNotificationMessages } from './notification'; +import { sendEmailNotifications } from './email'; class ReviewController { constructor() {} async getReviews(req, res) { - const idString = parseInt(req.params.reviewId) || ''; + const reviewId = parseInt(req.params.reviewId) || ''; - const data = await this.statementExecutionDB(idString); + const data = await this.getDataReviews(reviewId); return res.status(200).json({ 'success': true, @@ -14,28 +16,58 @@ class ReviewController { }); } - async statementExecutionDB(idString) { + async updateStateReviews(req, res) { + const reviewId = parseInt(req.params.reviewId); + const { activeflag } = req.body; + + if (!activeflag) { + process.stdout.write(`ReviewController.updateReviews : activeflag missing`); + throw new Error(`An error occurred : activeflag missing`); + } + const statusReview = activeflag === 'approve' ? 'active' : activeflag; + + const review = await this.updateStateDataReviews({ reviewID: reviewId }, { activeflag: statusReview }); + + await storeNotificationMessages(review); + // Send email notififcation of approval to authors and admins who have opted in + await sendEmailNotifications(review, activeflag); + + return res.status(200).json({ + 'success': true + }); + } + + async updateStateDataReviews(filter, update) { + try { + return await Reviews.findOneAndUpdate(filter, update, { + new: true + }); + } catch (err) { + process.stdout.write(`ReviewController.updateDataReviews : ${err.message}`); + throw new Error(`An error occurred : ${err.message}`); + } + } + + async getDataReviews(reviewId) { try { - const pipeline = this.reviewDynamicPipeline(idString); + const pipeline = this.reviewAggregatePipeline(reviewId); const statement = Reviews.aggregate(pipeline); return await statement.exec(); } catch (err) { - process.stdout.write(`ReviewController.statementExecutionDB : ${err.message}`); + process.stdout.write(`ReviewController.getDataReviews : ${err.message}`); throw new Error(`An error occurred : ${err.message}`); } } - reviewDynamicPipeline(idString) { + reviewAggregatePipeline(reviewId) { let query = []; - if (idString) { - query.push({ $match: { 'reviewID': idString } }); + if (reviewId) { + query.push({ $match: { 'reviewID': reviewId } }); } query.push({ "$lookup": { "from": "tools", "localField": "reviewerID", "foreignField": "id", "as": "person" } }); query.push({ "$lookup": { "from": "tools", "localField": "toolID", "foreignField": "id", "as": "tool" } }); - console.log(JSON.stringify(query)); - return query; } diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index e5a84a62..b1e67865 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -10,4 +10,11 @@ const router = express.Router(); // @access Private router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewController.getReviews(req, res)); + +// @router PATCH /api/v3/reviews/:reviewId +// @bodyParam {string} activeflag can be: active/approve (approve will be converted in active), reject, archive +// @desc get all reviews or find reviews by reviewId +// @access Private +router.patch('/:reviewId', passport.authenticate('jwt'), (req, res) => ReviewController.updateReviews(req, res)); + module.exports = router; From 57d23213839fd3243d9781fedff6b6ebe8d1dc50 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:10:12 +0000 Subject: [PATCH 016/229] cosmetic change --- src/resources/review/v3/review.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/review.controller.js b/src/resources/review/v3/review.controller.js index 3b69fb43..e26e0a9f 100644 --- a/src/resources/review/v3/review.controller.js +++ b/src/resources/review/v3/review.controller.js @@ -30,7 +30,7 @@ class ReviewController { await storeNotificationMessages(review); // Send email notififcation of approval to authors and admins who have opted in - await sendEmailNotifications(review, activeflag); + await sendEmailNotifications(review, activeflag); return res.status(200).json({ 'success': true From d2e2c8e888c45559957c647e0499f0af4b3d339d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:19:25 +0000 Subject: [PATCH 017/229] remove email hardcoded value from reviews v3 area --- src/resources/review/v3/email.js | 93 +++++++++++++++++++++++++ src/resources/review/v3/notification.js | 37 ++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/resources/review/v3/email.js create mode 100644 src/resources/review/v3/notification.js diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js new file mode 100644 index 00000000..a93a57a6 --- /dev/null +++ b/src/resources/review/v3/email.js @@ -0,0 +1,93 @@ +import { Data } from '../../tool/data.model'; +import { UserModel } from '../../user/user.model'; +import emailGenerator from '../../utilities/emailGenerator.util'; + +export const sendEmailNotifications = async (review, activeflag) => { + const tool = await Data.findOne({ id: review.toolID }); + const reviewer = await UserModel.findOne({ id: review.reviewerID }); + const toolLink = process.env.homeURL + '/tool/' + tool.id; + const hdrukEmail = process.env.HDRUK_ENQUIRY_EMAIL || `enquiry@healthdatagateway.org`; + + var q = UserModel.aggregate([ + { $match: { $or: [{ role: 'Admin' }, { id: { $in: tool.authors } }] } }, + { + $lookup: { + from: 'tools', + localField: 'id', + foreignField: 'id', + as: 'tool', + }, + }, + { $match: { 'tool.emailNotifications': true } }, + { + $project: { + _id: 1, + firstname: 1, + lastname: 1, + email: 1, + role: 1, + 'tool.emailNotifications': 1, + }, + }, + ]); + + q.exec((err, emailRecipients) => { + if (err) { + return new Error({ success: false, error: err }); + } + + let subject; + if (activeflag === 'active') { + subject = `A review has been added to the ${tool.type} ${tool.name}`; + } else if (activeflag === 'rejected') { + subject = `A review on the ${tool.type} ${tool.name} has been rejected`; + } else if (activeflag === 'archive') { + subject = `A review on the ${tool.type} ${tool.name} has been archived`; + } + + let html = `
+
+ + + + + + + + + + + + + + +
+ ${subject} +
+

+ ${ + activeflag === 'active' + ? `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to the tool ${tool.name}.` + : activeflag === 'rejected' + ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been rejected.` + : activeflag === 'archive' + ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been archived.` + : `` + } +

+
+ View ${tool.type} +
+
+
`; + + emailGenerator.sendEmail(emailRecipients, `${hdrukEmail}`, subject, html, false); + }); +} \ No newline at end of file diff --git a/src/resources/review/v3/notification.js b/src/resources/review/v3/notification.js new file mode 100644 index 00000000..4977a035 --- /dev/null +++ b/src/resources/review/v3/notification.js @@ -0,0 +1,37 @@ +import { Data } from '../../tool/data.model'; +import { UserModel } from '../../user/user.model'; +import { MessagesModel } from '../../message/message.model'; + +export const storeNotificationMessages = async (review) => { + const tool = await Data.findOne({ id: review.toolID }); + const reviewer = await UserModel.findOne({ id: review.reviewerID }); + const toolLink = process.env.homeURL + '/tool/' + review.toolID + '/' + tool.name; + const messageId = parseInt(Math.random().toString().replace('0.', '')); + + let message = new MessagesModel(); + message.messageID = messageId; + message.messageTo = 0; + message.messageObjectID = review.toolID; + message.messageType = 'review'; + message.messageSent = Date.now(); + message.isRead = false; + message.messageDescription = `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to your tool ${tool.name} ${toolLink}`; + + await message.save(async err => { + if (err) { + return new Error({ success: false, error: err }); + } + }); + + const authors = tool.authors; + authors.forEach(async author => { + message.messageTo = author; + await message.save(async err => { + if (err) { + return new Error({ success: false, error: err }); + } + }); + }); + + return { success: true, id: messageId }; +} \ No newline at end of file From 9f0a701195e36bb9d1c58b5164219954127e8e1d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:19:55 +0000 Subject: [PATCH 018/229] cosmetic change --- src/resources/review/v3/email.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index a93a57a6..5c139edb 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -3,9 +3,9 @@ import { UserModel } from '../../user/user.model'; import emailGenerator from '../../utilities/emailGenerator.util'; export const sendEmailNotifications = async (review, activeflag) => { - const tool = await Data.findOne({ id: review.toolID }); - const reviewer = await UserModel.findOne({ id: review.reviewerID }); - const toolLink = process.env.homeURL + '/tool/' + tool.id; + const tool = await Data.findOne({ id: review.toolID }); + const reviewer = await UserModel.findOne({ id: review.reviewerID }); + const toolLink = process.env.homeURL + '/tool/' + tool.id; const hdrukEmail = process.env.HDRUK_ENQUIRY_EMAIL || `enquiry@healthdatagateway.org`; var q = UserModel.aggregate([ From 6d20489408f1634a4461b8d915f556fb8803fcd7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:20:15 +0000 Subject: [PATCH 019/229] cosmetic change --- src/resources/review/v3/email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index 5c139edb..7513b6b1 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -8,7 +8,7 @@ export const sendEmailNotifications = async (review, activeflag) => { const toolLink = process.env.homeURL + '/tool/' + tool.id; const hdrukEmail = process.env.HDRUK_ENQUIRY_EMAIL || `enquiry@healthdatagateway.org`; - var q = UserModel.aggregate([ + const q = UserModel.aggregate([ { $match: { $or: [{ role: 'Admin' }, { id: { $in: tool.authors } }] } }, { $lookup: { From d8373968b3cef175f7268fc8faf72cbd51a9ceec Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:20:32 +0000 Subject: [PATCH 020/229] cosmetic change --- src/resources/review/v3/email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index 7513b6b1..67e69b48 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -8,7 +8,7 @@ export const sendEmailNotifications = async (review, activeflag) => { const toolLink = process.env.homeURL + '/tool/' + tool.id; const hdrukEmail = process.env.HDRUK_ENQUIRY_EMAIL || `enquiry@healthdatagateway.org`; - const q = UserModel.aggregate([ + const q = UserModel.aggregate([ { $match: { $or: [{ role: 'Admin' }, { id: { $in: tool.authors } }] } }, { $lookup: { From d1b071731ea2cc9c2eb29d25ea966aefdd73f2f6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:21:54 +0000 Subject: [PATCH 021/229] cosmetic change --- src/resources/review/v3/email.js | 46 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index 67e69b48..a7d387d0 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -8,30 +8,30 @@ export const sendEmailNotifications = async (review, activeflag) => { const toolLink = process.env.homeURL + '/tool/' + tool.id; const hdrukEmail = process.env.HDRUK_ENQUIRY_EMAIL || `enquiry@healthdatagateway.org`; - const q = UserModel.aggregate([ - { $match: { $or: [{ role: 'Admin' }, { id: { $in: tool.authors } }] } }, - { - $lookup: { - from: 'tools', - localField: 'id', - foreignField: 'id', - as: 'tool', - }, - }, - { $match: { 'tool.emailNotifications': true } }, - { - $project: { - _id: 1, - firstname: 1, - lastname: 1, - email: 1, - role: 1, - 'tool.emailNotifications': 1, - }, - }, - ]); + const statement = UserModel.aggregate([ + { $match: { $or: [{ role: 'Admin' }, { id: { $in: tool.authors } }] } }, + { + $lookup: { + from: 'tools', + localField: 'id', + foreignField: 'id', + as: 'tool', + }, + }, + { $match: { 'tool.emailNotifications': true } }, + { + $project: { + _id: 1, + firstname: 1, + lastname: 1, + email: 1, + role: 1, + 'tool.emailNotifications': 1, + }, + }, + ]); - q.exec((err, emailRecipients) => { + statement.exec((err, emailRecipients) => { if (err) { return new Error({ success: false, error: err }); } From 99badd584898ac43c76d3edd6a62f922aadaa96d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:22:49 +0000 Subject: [PATCH 022/229] cosmetic change --- src/resources/review/v3/notification.js | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/resources/review/v3/notification.js b/src/resources/review/v3/notification.js index 4977a035..58dc4b2a 100644 --- a/src/resources/review/v3/notification.js +++ b/src/resources/review/v3/notification.js @@ -3,35 +3,35 @@ import { UserModel } from '../../user/user.model'; import { MessagesModel } from '../../message/message.model'; export const storeNotificationMessages = async (review) => { - const tool = await Data.findOne({ id: review.toolID }); - const reviewer = await UserModel.findOne({ id: review.reviewerID }); - const toolLink = process.env.homeURL + '/tool/' + review.toolID + '/' + tool.name; + const tool = await Data.findOne({ id: review.toolID }); + const reviewer = await UserModel.findOne({ id: review.reviewerID }); + const toolLink = process.env.homeURL + '/tool/' + review.toolID + '/' + tool.name; const messageId = parseInt(Math.random().toString().replace('0.', '')); - let message = new MessagesModel(); - message.messageID = messageId; - message.messageTo = 0; - message.messageObjectID = review.toolID; - message.messageType = 'review'; - message.messageSent = Date.now(); - message.isRead = false; - message.messageDescription = `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to your tool ${tool.name} ${toolLink}`; + let message = new MessagesModel(); + message.messageID = messageId; + message.messageTo = 0; + message.messageObjectID = review.toolID; + message.messageType = 'review'; + message.messageSent = Date.now(); + message.isRead = false; + message.messageDescription = `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to your tool ${tool.name} ${toolLink}`; - await message.save(async err => { - if (err) { - return new Error({ success: false, error: err }); - } - }); + await message.save(async err => { + if (err) { + return new Error({ success: false, error: err }); + } + }); - const authors = tool.authors; - authors.forEach(async author => { - message.messageTo = author; - await message.save(async err => { - if (err) { - return new Error({ success: false, error: err }); - } - }); - }); + const authors = tool.authors; + authors.forEach(async author => { + message.messageTo = author; + await message.save(async err => { + if (err) { + return new Error({ success: false, error: err }); + } + }); + }); - return { success: true, id: messageId }; + return { success: true, id: messageId }; } \ No newline at end of file From 4911ab2f6143842255bc4a34e7033aab81f49765 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:23:30 +0000 Subject: [PATCH 023/229] cosmetic change --- src/resources/review/v3/email.js | 80 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index a7d387d0..ecf4d94d 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -46,46 +46,46 @@ export const sendEmailNotifications = async (review, activeflag) => { } let html = `
-
- - - - - - - - - - - - - - -
- ${subject} -
-

- ${ - activeflag === 'active' - ? `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to the tool ${tool.name}.` - : activeflag === 'rejected' - ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been rejected.` - : activeflag === 'archive' - ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been archived.` - : `` - } -

-
- View ${tool.type} -
-
+
+ + + + + + + + + + + + + + +
+ ${subject} +
+

+ ${ + activeflag === 'active' + ? `${reviewer.firstname} ${reviewer.lastname} gave a ${review.rating}-star review to the tool ${tool.name}.` + : activeflag === 'rejected' + ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been rejected.` + : activeflag === 'archive' + ? `A ${review.rating}-star review from ${reviewer.firstname} ${reviewer.lastname} on the ${tool.type} ${tool.name} has been archived.` + : `` + } +

+
+ View ${tool.type} +
+
`; emailGenerator.sendEmail(emailRecipients, `${hdrukEmail}`, subject, html, false); From d63e1ccfebef2deeaec4d9939face6aa6cf505bf Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:23:51 +0000 Subject: [PATCH 024/229] cosmetic change --- src/resources/review/v3/email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index ecf4d94d..30211c7c 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -86,7 +86,7 @@ export const sendEmailNotifications = async (review, activeflag) => { - `; + `; emailGenerator.sendEmail(emailRecipients, `${hdrukEmail}`, subject, html, false); }); From c7e3f9ea458ba95ffaa25d73f366086b4e1b94ba Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 12:24:53 +0000 Subject: [PATCH 025/229] cosmetic change --- src/resources/review/v3/email.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index 30211c7c..2771e9cf 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -32,20 +32,20 @@ export const sendEmailNotifications = async (review, activeflag) => { ]); statement.exec((err, emailRecipients) => { - if (err) { - return new Error({ success: false, error: err }); - } + if (err) { + return new Error({ success: false, error: err }); + } - let subject; - if (activeflag === 'active') { - subject = `A review has been added to the ${tool.type} ${tool.name}`; - } else if (activeflag === 'rejected') { - subject = `A review on the ${tool.type} ${tool.name} has been rejected`; - } else if (activeflag === 'archive') { - subject = `A review on the ${tool.type} ${tool.name} has been archived`; - } + let subject; + if (activeflag === 'active') { + subject = `A review has been added to the ${tool.type} ${tool.name}`; + } else if (activeflag === 'rejected') { + subject = `A review on the ${tool.type} ${tool.name} has been rejected`; + } else if (activeflag === 'archive') { + subject = `A review on the ${tool.type} ${tool.name} has been archived`; + } - let html = `
+ let html = `
Date: Wed, 30 Nov 2022 12:25:14 +0000 Subject: [PATCH 026/229] cosmetic change --- src/resources/review/v3/email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/email.js b/src/resources/review/v3/email.js index 2771e9cf..0b4a8bb0 100644 --- a/src/resources/review/v3/email.js +++ b/src/resources/review/v3/email.js @@ -88,6 +88,6 @@ export const sendEmailNotifications = async (review, activeflag) => { `; - emailGenerator.sendEmail(emailRecipients, `${hdrukEmail}`, subject, html, false); + emailGenerator.sendEmail(emailRecipients, `${hdrukEmail}`, subject, html, false); }); } \ No newline at end of file From aac96cc79cfc5fff1683ab4844a772f13884f0d0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 30 Nov 2022 15:02:24 +0000 Subject: [PATCH 027/229] cosmetic update --- src/resources/review/v3/review.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index b1e67865..d376b5f3 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -13,7 +13,7 @@ router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewCont // @router PATCH /api/v3/reviews/:reviewId // @bodyParam {string} activeflag can be: active/approve (approve will be converted in active), reject, archive -// @desc get all reviews or find reviews by reviewId +// @desc update active flag by reviewId // @access Private router.patch('/:reviewId', passport.authenticate('jwt'), (req, res) => ReviewController.updateReviews(req, res)); From f899c8df17171f63b5fa50742e804f6a5b6c0a30 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 10:26:50 +0000 Subject: [PATCH 028/229] tech debt added v3 for message endpoints --- src/config/server.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/server.js b/src/config/server.js index 11a29f32..60b5c46f 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -166,7 +166,9 @@ app.use('/api/v1/topics', require('../resources/topic/topic.route')); app.use('/api/v1/publishers', require('../resources/publisher/publisher.route')); app.use('/api/v1/teams', require('../resources/team/team.route')); app.use('/api/v1/workflows', require('../resources/workflow/workflow.route')); + app.use('/api/v1/messages', require('../resources/message/message.route')); +app.use('/api/v3/messages', require('../resources/message/v3/message.route')); app.use('/api/v1/reviews', require('../resources/review/v1/review.route')); app.use('/api/v3/reviews', require('../resources/review/v3/review.route')); From 87010e0b1e04da7598e690411ebdc502ac3f586b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 10:35:19 +0000 Subject: [PATCH 029/229] v3 endpoints --- .../message/v3/message.controller.js | 331 ++++++++++++++++++ src/resources/message/v3/message.route.js | 36 ++ src/resources/message/v3/notification.js | 121 +++++++ 3 files changed, 488 insertions(+) create mode 100644 src/resources/message/v3/message.controller.js create mode 100644 src/resources/message/v3/message.route.js create mode 100644 src/resources/message/v3/notification.js diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js new file mode 100644 index 00000000..6311e147 --- /dev/null +++ b/src/resources/message/v3/message.controller.js @@ -0,0 +1,331 @@ +import _ from 'lodash'; +import mongoose from 'mongoose'; + +import { activityLogService } from '../../activitylog/dependency'; +import constants from '../../utilities/constants.util'; +import { Data as ToolModel } from '../../tool/data.model'; +import { dataRequestService } from '../../datarequest/dependency'; +import { MessagesModel } from './../message.model'; +import { ROLES } from '../../user/user.roles'; +import { TopicModel } from '../../topic/topic.model'; +import teamController from '../../team/team.controller'; +import { sendNotification, sendPubSubMessage } from './notification'; + +const topicController = require('../../topic/topic.controller'); + + +class MessageController +{ + limitRows = 50; + roles = [ROLES.Admin.toLowerCase(), ROLES.Creator.toLowerCase()]; + + constructor() {} + + async createMessage(req, res) { + try { + const { _id: createdBy, firstname, lastname, isServiceAccount = false } = req.user; + let { messageType = 'message', topic = '', messageDescription, relatedObjectIds, firstMessage } = req.body; + let topicObj = {}; + let team, publisher, userType; + let tools = {}; + + const userRole = req.user.role.toLowerCase(); + + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } + + // 1. If the message type is 'message' and topic id is empty + if (messageType === 'message') { + // 2. Find the related object(s) in MongoDb and include team data to update topic recipients in case teams have changed + tools = await ToolModel.find() + .where('_id') + .in(relatedObjectIds) + .populate({ + path: 'publisher', + populate: { + path: 'team', + select: 'members notifications', + populate: { + path: 'users', + }, + }, + }); + + if (_.isEmpty(tools)) return undefined; + + ({ publisher = '' } = tools[0]); + + if (_.isEmpty(publisher)) { + process.stdout.write(`No publisher associated to this dataset\n`); + return res.status(500).json({ success: false, message: 'No publisher associated to this dataset' }); + } + + ({ team = [] } = publisher); + if (_.isEmpty(team)) { + process.stdout.write(`No team associated to publisher, cannot message\n`); + return res.status(500).json({ success: false, message: 'No team associated to publisher, cannot message' }); + } + + userType = teamController.checkTeamPermissions('', team.toObject(), req.user._id) + ? constants.userTypes.CUSTODIAN + : constants.userTypes.APPLICANT; + if (_.isEmpty(topic)) { + topicObj = await topicController.buildTopic({ createdBy, relatedObjectIds }); + if (!topicObj) return res.status(500).json({ success: false, message: 'Could not save topic to database.' }); + topic = topicObj._id; + } else { + topicObj = await topicController.findTopic(topic, createdBy); + if (!topicObj) return res.status(404).json({ success: false, message: 'The topic specified could not be found' }); + topicObj.recipients = await topicController.buildRecipients(team, topicObj.createdBy); + await topicObj.save(); + } + + if (!topicObj.linkedDataAccessApplication) { + const accessRecord = await dataRequestService.linkRelatedApplicationByMessageContext( + topicObj._id, + req.user.id, + topicObj.datasets.map(dataset => dataset.datasetId), + constants.applicationStatuses.INPROGRESS + ); + if (accessRecord) { + topicObj.linkedDataAccessApplication = accessRecord._id; + await topicObj.save(); + } + } + } + + const message = await MessagesModel.create({ + messageID: parseInt(Math.random().toString().replace('0.', '')), + messageObjectID: parseInt(Math.random().toString().replace('0.', '')), + messageDescription, + firstMessage, + topic, + createdBy, + messageType, + readBy: [new mongoose.Types.ObjectId(createdBy)], + ...(userType && { userType }), + }); + + if (!message) return res.status(500).json({ success: false, message: 'Could not save message to database.' }); + + if (messageType === 'message') { + await sendNotification(topicObj, team); + await sendPubSubMessage(tools, topicObj._id, message, req.body.firstMessage, isServiceAccount); + } + // 19. Return successful response with message data + const messageObj = message.toObject(); + messageObj.createdByName = { firstname, lastname }; + messageObj.createdBy = { _id: createdBy, firstname, lastname }; + + // 20. Update activity log if there is a linked DAR + if (topicObj.linkedDataAccessApplication) { + activityLogService.logActivity(constants.activityLogEvents.data_access_request.PRESUBMISSION_MESSAGE, { + messages: [messageObj], + applicationId: topicObj.linkedDataAccessApplication, + publisher: publisher.name, + }); + } + + return res.status(201).json({ success: true, messageObj }); + } catch (err) { + process.stdout.write(`MESSAGE - createMessage : ${err.message}\n`); + return res.status(500).json(err.message); + } + } + + async getCountUnreadMessagesByPersonId(req, res) { + const personId = parseInt(req.params.personId) || ''; + const userRole = req.user.role.toLowerCase(); + + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - getCountUnreadMessagesByPersonId : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } + + const pipeline = this.messagesAggregatePipelineCount(userRole, personId); + const messages = await this.getDataMessages(pipeline); + + const countUnreadMessages = messages.filter(element => element.isRead !== 'false').length; + + return res.status(200).json({ countUnreadMessages }); + } + + async getMessagesByPersonId(req, res) { + const personId = parseInt(req.params.personId) || ''; + const userRole = req.user.role.toLowerCase(); + + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - getMessagesByPersonId : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } + + const pipeline = this.messagesAggregatePipelineByPersonId(userRole, personId); + const messages = await this.getDataMessages(pipeline); + + return res.status(200).json({ success: true, data: messages }); + } + + async getUnreadMessageCount(req, res){ + try { + const { _id: userId } = req.user; + let unreadMessageCount = 0; + + const topics = await TopicModel.find({ + recipients: { $elemMatch: { $eq: userId } }, + status: 'active', + }); + + topics.forEach(topic => { + topic.topicMessages.forEach(message => { + if (!message.readBy.includes(userId)) { + unreadMessageCount++; + } + }); + }); + + return res.status(200).json({ success: true, count: unreadMessageCount }); + } catch (err) { + process.stdout.write(`MESSAGE - getUnreadMessageCount : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } + } + + async deleteMessage(req, res) { + try { + const { id } = req.params; + const { _id: userId } = req.user; + + const userRole = req.user.role.toLowerCase(); + + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } + + if (!id) { + return res.status(404).json({ success: false, message: 'Message Id not found.' }); + } + + const message = await MessagesModel.findOne({ _id: id }); + + if (!message) { + return res.status(404).json({ success: false, message: 'Message not found for ${id}' }); + } + + if (message.createdBy.toString() !== userId.toString()) { + return res.status(401).json({ success: false, message: 'Not authorised to delete this message' }); + } + + await MessagesModel.remove({ _id: id }); + + const messagesCount = await MessagesModel.count({ topic: message.topic }); + + if (!messagesCount) { + await TopicModel.remove({ _id: new mongoose.Types.ObjectId(message.topic) }); + } + + return res.status(204).json({ success: true }); + } catch (err) { + process.stdout.write(`MESSAGE - deleteMessage : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } + } + + async updateMessage(req, res) { + try { + let { _id: userId } = req.user; + let { messageId, isRead, messageDescription = '', messageType = '' } = req.body; + + const userRole = req.user.role.toLowerCase(); + + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } + + if (!messageId) return res.status(404).json({ success: false, message: 'Message Id not found.' }); + + const message = await MessagesModel.findOne({ _id: messageId }); + + if (!message) { + return res.status(404).json({ success: false, message: 'Message not found.' }); + } + + if (isRead && !message.readBy.includes(userId.toString())) { + message.readBy.push(userId); + } + + if (isRead) { + message.isRead = isRead; + } + + if (!_.isEmpty(messageDescription)) { + message.messageDescription = messageDescription; + } + + if (!_.isEmpty(messageType)) { + message.messageType = messageType; + } + + await message.save(); + + return res.status(204).json({ success: true }); + } catch (err) { + process.stdout.write(`MESSAGE - updateMessage : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } + } + + async getDataMessages(pipeline) { + try { + const statement = MessagesModel.aggregate(pipeline).limit(this.limitRows); + return await statement.exec(); + } catch (err) { + process.stdout.write(`MessageController.getDataReviews : ${err.message}`); + throw new Error(`An error occurred : ${err.message}`); + } + } + + messagesAggregatePipelineByPersonId(role, personId) { + let query = []; + + if (role === ROLES.Admin.toLowerCase()) { + query.push({ "$match": { "$and": [{ "$or": [{ "messageTo": personId }, { "messageTo": 0 }] }] } }); + } + + if (role === ROLES.Creator.toLowerCase()) { + query.push({ "$match": { "$and": [{ "messageTo": personId }] } }); + } + + query.push({ "$sort": { "createdDate": -1 } }); + query.push({ "$lookup": { "from": "tools", "localField": "messageObjectID", "foreignField": "id", "as": "tool" } }); + query.push({ "$lookup": { "from": "course", "localField": "messageObjectID", "foreignField": "id", "as": "course" } }); + + return query; + } + + messagesAggregatePipelineCount(role, personId) { + let query = []; + + if (role === ROLES.Admin.toLowerCase()) { + query.push({ "$match": { "$and": [{ "$or": [{ "messageTo": personId }, { "messageTo": 0 }] }] } }); + } + + if (role === ROLES.Creator.toLowerCase()) { + query.push({ "$match": { "$and": [{ "messageTo": personId }] } }); + } + + query.push({ "$sort": { "createdDate": -1 } }); + query.push({ "$lookup": { "from": "tools", "localField": "messageObjectID", "foreignField": "id", "as": "tool" } }); + + return query; + } + + checkUserRole(userRole) { + return (this.roles.indexOf(userRole) > -1) + } +} + +module.exports = new MessageController(); \ No newline at end of file diff --git a/src/resources/message/v3/message.route.js b/src/resources/message/v3/message.route.js new file mode 100644 index 00000000..4031aefa --- /dev/null +++ b/src/resources/message/v3/message.route.js @@ -0,0 +1,36 @@ +import express from 'express'; +import passport from 'passport'; +const MessageController = require('./message.controller'); +const router = express.Router(); + +// @router GET /api/v3/messages/:personId +// @desc get messages by person id for users.role === Admin or users.role === Creator +// @access Private +router.get('/:personId', passport.authenticate('jwt'), (req, res) => MessageController.getMessagesByPersonId(req, res)); + +// @router GET /api/v3/messages/:personId/unread/count +// @desc count number of unread messages by person id for users.role === Admin or users.role === Creator +// @access Private +router.get('/:personId/unread/count', passport.authenticate('jwt'), (req, res) => MessageController.getCountUnreadMessagesByPersonId(req, res)); + +// @route GET api/v3/messages/unread/count +// @desc GET the number of unread messages for a user +// @access Private +router.get('/unread/count', passport.authenticate('jwt'), (req, res) => MessageController.getUnreadMessageCount(req, res)); + +// @route POST api/v3/messages +// @desc POST a message - create an message/enquire; user need to be Admin or Creator +// @access Private +router.post('/', passport.authenticate('jwt'), (req, res) => MessageController.createMessage(req, res)); + +// @route PUT api/messages +// @desc PUT Update a message; user need to be Admin or Creator +// @access Private +router.put('/', passport.authenticate('jwt'), (req, res) => MessageController.updateMessage(req, res)); + +// @route DELETE api/v3/messages/:id +// @desc DELETE Delete a message; user need to be Admin or Creator +// @access Private +router.delete('/:id', passport.authenticate('jwt'), (req, res) => MessageController.deleteMessage(req, res)); + +module.exports = router; \ No newline at end of file diff --git a/src/resources/message/v3/notification.js b/src/resources/message/v3/notification.js new file mode 100644 index 00000000..020a3287 --- /dev/null +++ b/src/resources/message/v3/notification.js @@ -0,0 +1,121 @@ +import _ from 'lodash'; +import mongoose from 'mongoose'; + +import constants from '../../utilities/constants.util'; +import emailGenerator from '../../utilities/emailGenerator.util'; +import { publishMessageWithRetryToPubSub } from '../../../services/google/PubSubWithRetryService'; +import { PublisherModel } from '../../publisher/publisher.model'; +import teamController from '../../team/team.controller'; +import { UserModel } from '../../user/user.model'; + +const { ObjectId } = require('mongodb'); + +export const sendNotification = async (topicObj, team) => { + let optIn, subscribedEmails, messageCreatorRecipient; + // 16. Find recipients who have opted in to email updates and exclude the requesting user + let messageRecipients = await UserModel.find({ _id: { $in: topicObj.recipients } }); + + if (!_.isEmpty(team) || !_.isNil(team)) { + // 17. team all users for notificationType + generic email + // Retrieve notifications for the team based on type return {notificationType, subscribedEmails, optIn} + let teamNotifications = teamController.getTeamNotificationByType(team, constants.teamNotificationTypes.DATAACCESSREQUEST); + // only deconstruct if team notifications object returns - safeguard code + if (!_.isEmpty(teamNotifications)) { + // Get teamNotification emails if optIn true + ({ optIn = false, subscribedEmails = [] } = teamNotifications); + // check subscribedEmails and optIn send back emails or blank [] + let teamNotificationEmails = teamController.getTeamNotificationEmails(optIn, subscribedEmails); + // get users from team.members with notification type and optedIn only + const subscribedMembersByType = teamController.filterMembersByNoticationTypesOptIn( + [...team.members], + [constants.teamNotificationTypes.DATAACCESSREQUEST] + ); + if (!_.isEmpty(subscribedMembersByType)) { + // build cleaner array of memberIds from subscribedMembersByType + if (topicObj.topicMessages !== undefined) { + const memberIds = [...subscribedMembersByType.map(m => m.memberid.toString()), ...topicObj.createdBy._id.toString()]; + // returns array of objects [{email: 'email@email.com '}] for members in subscribed emails users is list of full user object + const { memberEmails } = teamController.getMemberDetails([...memberIds], [...messageRecipients]); + messageRecipients = [...teamNotificationEmails, ...memberEmails]; + } else { + const memberIds = [...subscribedMembersByType.map(m => m.memberid.toString())].filter( + ele => ele !== topicObj.createdBy.toString() + ); + const creatorObjectId = topicObj.createdBy.toString(); + // returns array of objects [{email: 'email@email.com '}] for members in subscribed emails users is list of full user object + const { memberEmails } = teamController.getMemberDetails([...memberIds], [...messageRecipients]); + const creatorEmail = await UserModel.findById(creatorObjectId); + messageCreatorRecipient = [{ email: creatorEmail.email }]; + messageRecipients = [...teamNotificationEmails, ...memberEmails]; + } + } else { + // only if not membersByType but has a team email setup + messageRecipients = [...messageRecipients, ...teamNotificationEmails]; + } + } + } + + // Create object to pass through email data + let options = { + firstMessage, + firstname, + lastname, + messageDescription, + openMessagesLink: process.env.homeURL + '/search?search=&tab=Datasets&openUserMessages=true', + }; + // Create email body content + let html = emailGenerator.generateMessageNotification(options); + + // 18. Send email + emailGenerator.sendEmail( + messageRecipients, + constants.hdrukEmail, + `You have received a new message on the HDR UK Innovation Gateway`, + html, + false + ); + + if (messageCreatorRecipient) { + let htmlCreator = emailGenerator.generateMessageCreatorNotification(options); + + emailGenerator.sendEmail( + messageCreatorRecipient, + constants.hdrukEmail, + `You have received a new message on the HDR UK Innovation Gateway`, + htmlCreator, + false + ); + } +} + +export const sendPubSubMessage = async (tools, topicObjId, message, firstMessag, isServiceAccount) => { + try { + const cacheEnabled = parseInt(process.env.CACHE_ENABLED) || 0; + + if (cacheEnabled && !isServiceAccount) { + let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); + + if (publisherDetails['dar-integration'] && publisherDetails['dar-integration']['enabled']) { + const pubSubMessage = { + id: '', + type: 'enquiry', + publisherInfo: { + id: publisherDetails._id, + name: publisherDetails.name, + }, + details: { + topicId: topicObjId, + messageId: message.messageID, + createdDate: message.createdDate, + questionBank: firstMessage, + }, + darIntegration: publisherDetails['dar-integration'], + }; + await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); + } + } + } catch (err) { + process.stdout.write(`MESSAGE - createMessage - send PubSub Message : ${err.message}\n`); + return res.status(500).json(err.message); + } +} \ No newline at end of file From 8e3ab789f45fcb2473c0bb82cec4bcb99343bfc7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 10:38:37 +0000 Subject: [PATCH 030/229] cosmetic update - alignment --- src/resources/message/v3/message.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index 6311e147..3501d0d8 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -167,6 +167,7 @@ class MessageController return res.status(200).json({ success: true, data: messages }); } + async getUnreadMessageCount(req, res){ try { const { _id: userId } = req.user; From 22b57da91894924064457d1f5a55431c94204230 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 10:52:41 +0000 Subject: [PATCH 031/229] cosmetic update - alignment --- src/resources/message/v3/message.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index 3501d0d8..e71bc167 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -167,9 +167,9 @@ class MessageController return res.status(200).json({ success: true, data: messages }); } - + async getUnreadMessageCount(req, res){ - try { + try { const { _id: userId } = req.user; let unreadMessageCount = 0; From 94b6380a12b7e967d89057d6bf9f2fb5d38e2ce7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 10:58:40 +0000 Subject: [PATCH 032/229] cosmetic update - alignment --- .../message/v3/message.controller.js | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index e71bc167..779ce870 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -153,22 +153,21 @@ class MessageController } async getMessagesByPersonId(req, res) { - const personId = parseInt(req.params.personId) || ''; - const userRole = req.user.role.toLowerCase(); + const personId = parseInt(req.params.personId) || ''; + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - getMessagesByPersonId : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - getMessagesByPersonId : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } - const pipeline = this.messagesAggregatePipelineByPersonId(userRole, personId); - const messages = await this.getDataMessages(pipeline); + const pipeline = this.messagesAggregatePipelineByPersonId(userRole, personId); + const messages = await this.getDataMessages(pipeline); - return res.status(200).json({ success: true, data: messages }); + return res.status(200).json({ success: true, data: messages }); } - - async getUnreadMessageCount(req, res){ + async getUnreadMessageCount(req, res){ try { const { _id: userId } = req.user; let unreadMessageCount = 0; @@ -198,16 +197,16 @@ class MessageController const { id } = req.params; const { _id: userId } = req.user; - const userRole = req.user.role.toLowerCase(); + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } if (!id) { - return res.status(404).json({ success: false, message: 'Message Id not found.' }); - } + return res.status(404).json({ success: false, message: 'Message Id not found.' }); + } const message = await MessagesModel.findOne({ _id: id }); From 03a0e6cdd2ea1d6e80036441262e2e58c398fc4f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 11:05:27 +0000 Subject: [PATCH 033/229] cosmetic update - alignment --- src/resources/message/v3/message.controller.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index 779ce870..0f5a10ad 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -238,12 +238,12 @@ class MessageController let { _id: userId } = req.user; let { messageId, isRead, messageDescription = '', messageType = '' } = req.body; - const userRole = req.user.role.toLowerCase(); + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } if (!messageId) return res.status(404).json({ success: false, message: 'Message Id not found.' }); From d17ef11d34455d97b62dbb4aa26f884453e6fffa Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 11:13:20 +0000 Subject: [PATCH 034/229] cosmetic update - alignment --- .../message/v3/message.controller.js | 396 +++++++++--------- 1 file changed, 198 insertions(+), 198 deletions(-) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index 0f5a10ad..fd2f0c18 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -21,13 +21,13 @@ class MessageController constructor() {} - async createMessage(req, res) { - try { - const { _id: createdBy, firstname, lastname, isServiceAccount = false } = req.user; - let { messageType = 'message', topic = '', messageDescription, relatedObjectIds, firstMessage } = req.body; - let topicObj = {}; - let team, publisher, userType; - let tools = {}; + async createMessage(req, res) { + try { + const { _id: createdBy, firstname, lastname, isServiceAccount = false } = req.user; + let { messageType = 'message', topic = '', messageDescription, relatedObjectIds, firstMessage } = req.body; + let topicObj = {}; + let team, publisher, userType; + let tools = {}; const userRole = req.user.role.toLowerCase(); @@ -36,104 +36,104 @@ class MessageController res.status(500).json(`An error occurred - user does not have the right roles`); } - // 1. If the message type is 'message' and topic id is empty - if (messageType === 'message') { - // 2. Find the related object(s) in MongoDb and include team data to update topic recipients in case teams have changed - tools = await ToolModel.find() - .where('_id') - .in(relatedObjectIds) - .populate({ - path: 'publisher', - populate: { - path: 'team', - select: 'members notifications', - populate: { - path: 'users', - }, - }, - }); - - if (_.isEmpty(tools)) return undefined; - - ({ publisher = '' } = tools[0]); - - if (_.isEmpty(publisher)) { - process.stdout.write(`No publisher associated to this dataset\n`); - return res.status(500).json({ success: false, message: 'No publisher associated to this dataset' }); - } - - ({ team = [] } = publisher); - if (_.isEmpty(team)) { - process.stdout.write(`No team associated to publisher, cannot message\n`); - return res.status(500).json({ success: false, message: 'No team associated to publisher, cannot message' }); - } - - userType = teamController.checkTeamPermissions('', team.toObject(), req.user._id) - ? constants.userTypes.CUSTODIAN - : constants.userTypes.APPLICANT; - if (_.isEmpty(topic)) { - topicObj = await topicController.buildTopic({ createdBy, relatedObjectIds }); - if (!topicObj) return res.status(500).json({ success: false, message: 'Could not save topic to database.' }); - topic = topicObj._id; - } else { - topicObj = await topicController.findTopic(topic, createdBy); - if (!topicObj) return res.status(404).json({ success: false, message: 'The topic specified could not be found' }); - topicObj.recipients = await topicController.buildRecipients(team, topicObj.createdBy); - await topicObj.save(); - } - - if (!topicObj.linkedDataAccessApplication) { - const accessRecord = await dataRequestService.linkRelatedApplicationByMessageContext( - topicObj._id, - req.user.id, - topicObj.datasets.map(dataset => dataset.datasetId), - constants.applicationStatuses.INPROGRESS - ); - if (accessRecord) { - topicObj.linkedDataAccessApplication = accessRecord._id; - await topicObj.save(); - } - } - } - - const message = await MessagesModel.create({ - messageID: parseInt(Math.random().toString().replace('0.', '')), - messageObjectID: parseInt(Math.random().toString().replace('0.', '')), - messageDescription, - firstMessage, - topic, - createdBy, - messageType, - readBy: [new mongoose.Types.ObjectId(createdBy)], - ...(userType && { userType }), - }); - - if (!message) return res.status(500).json({ success: false, message: 'Could not save message to database.' }); - - if (messageType === 'message') { - await sendNotification(topicObj, team); - await sendPubSubMessage(tools, topicObj._id, message, req.body.firstMessage, isServiceAccount); - } - // 19. Return successful response with message data - const messageObj = message.toObject(); - messageObj.createdByName = { firstname, lastname }; - messageObj.createdBy = { _id: createdBy, firstname, lastname }; - - // 20. Update activity log if there is a linked DAR - if (topicObj.linkedDataAccessApplication) { - activityLogService.logActivity(constants.activityLogEvents.data_access_request.PRESUBMISSION_MESSAGE, { - messages: [messageObj], - applicationId: topicObj.linkedDataAccessApplication, - publisher: publisher.name, - }); - } - - return res.status(201).json({ success: true, messageObj }); - } catch (err) { - process.stdout.write(`MESSAGE - createMessage : ${err.message}\n`); - return res.status(500).json(err.message); - } - } + // 1. If the message type is 'message' and topic id is empty + if (messageType === 'message') { + // 2. Find the related object(s) in MongoDb and include team data to update topic recipients in case teams have changed + tools = await ToolModel.find() + .where('_id') + .in(relatedObjectIds) + .populate({ + path: 'publisher', + populate: { + path: 'team', + select: 'members notifications', + populate: { + path: 'users', + }, + }, + }); + + if (_.isEmpty(tools)) return undefined; + + ({ publisher = '' } = tools[0]); + + if (_.isEmpty(publisher)) { + process.stdout.write(`No publisher associated to this dataset\n`); + return res.status(500).json({ success: false, message: 'No publisher associated to this dataset' }); + } + + ({ team = [] } = publisher); + if (_.isEmpty(team)) { + process.stdout.write(`No team associated to publisher, cannot message\n`); + return res.status(500).json({ success: false, message: 'No team associated to publisher, cannot message' }); + } + + userType = teamController.checkTeamPermissions('', team.toObject(), req.user._id) + ? constants.userTypes.CUSTODIAN + : constants.userTypes.APPLICANT; + if (_.isEmpty(topic)) { + topicObj = await topicController.buildTopic({ createdBy, relatedObjectIds }); + if (!topicObj) return res.status(500).json({ success: false, message: 'Could not save topic to database.' }); + topic = topicObj._id; + } else { + topicObj = await topicController.findTopic(topic, createdBy); + if (!topicObj) return res.status(404).json({ success: false, message: 'The topic specified could not be found' }); + topicObj.recipients = await topicController.buildRecipients(team, topicObj.createdBy); + await topicObj.save(); + } + + if (!topicObj.linkedDataAccessApplication) { + const accessRecord = await dataRequestService.linkRelatedApplicationByMessageContext( + topicObj._id, + req.user.id, + topicObj.datasets.map(dataset => dataset.datasetId), + constants.applicationStatuses.INPROGRESS + ); + if (accessRecord) { + topicObj.linkedDataAccessApplication = accessRecord._id; + await topicObj.save(); + } + } + } + + const message = await MessagesModel.create({ + messageID: parseInt(Math.random().toString().replace('0.', '')), + messageObjectID: parseInt(Math.random().toString().replace('0.', '')), + messageDescription, + firstMessage, + topic, + createdBy, + messageType, + readBy: [new mongoose.Types.ObjectId(createdBy)], + ...(userType && { userType }), + }); + + if (!message) return res.status(500).json({ success: false, message: 'Could not save message to database.' }); + + if (messageType === 'message') { + await sendNotification(topicObj, team); + await sendPubSubMessage(tools, topicObj._id, message, req.body.firstMessage, isServiceAccount); + } + // 19. Return successful response with message data + const messageObj = message.toObject(); + messageObj.createdByName = { firstname, lastname }; + messageObj.createdBy = { _id: createdBy, firstname, lastname }; + + // 20. Update activity log if there is a linked DAR + if (topicObj.linkedDataAccessApplication) { + activityLogService.logActivity(constants.activityLogEvents.data_access_request.PRESUBMISSION_MESSAGE, { + messages: [messageObj], + applicationId: topicObj.linkedDataAccessApplication, + publisher: publisher.name, + }); + } + + return res.status(201).json({ success: true, messageObj }); + } catch (err) { + process.stdout.write(`MESSAGE - createMessage : ${err.message}\n`); + return res.status(500).json(err.message); + } + } async getCountUnreadMessagesByPersonId(req, res) { const personId = parseInt(req.params.personId) || ''; @@ -153,129 +153,129 @@ class MessageController } async getMessagesByPersonId(req, res) { - const personId = parseInt(req.params.personId) || ''; - const userRole = req.user.role.toLowerCase(); + const personId = parseInt(req.params.personId) || ''; + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - getMessagesByPersonId : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - getMessagesByPersonId : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } - const pipeline = this.messagesAggregatePipelineByPersonId(userRole, personId); - const messages = await this.getDataMessages(pipeline); + const pipeline = this.messagesAggregatePipelineByPersonId(userRole, personId); + const messages = await this.getDataMessages(pipeline); - return res.status(200).json({ success: true, data: messages }); + return res.status(200).json({ success: true, data: messages }); } - async getUnreadMessageCount(req, res){ - try { - const { _id: userId } = req.user; - let unreadMessageCount = 0; - - const topics = await TopicModel.find({ - recipients: { $elemMatch: { $eq: userId } }, - status: 'active', - }); - - topics.forEach(topic => { - topic.topicMessages.forEach(message => { - if (!message.readBy.includes(userId)) { - unreadMessageCount++; - } - }); - }); - - return res.status(200).json({ success: true, count: unreadMessageCount }); - } catch (err) { - process.stdout.write(`MESSAGE - getUnreadMessageCount : ${err.message}\n`); - return res.status(500).json({ message: err.message }); - } + async getUnreadMessageCount(req, res){ + try { + const { _id: userId } = req.user; + let unreadMessageCount = 0; + + const topics = await TopicModel.find({ + recipients: { $elemMatch: { $eq: userId } }, + status: 'active', + }); + + topics.forEach(topic => { + topic.topicMessages.forEach(message => { + if (!message.readBy.includes(userId)) { + unreadMessageCount++; + } + }); + }); + + return res.status(200).json({ success: true, count: unreadMessageCount }); + } catch (err) { + process.stdout.write(`MESSAGE - getUnreadMessageCount : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } } async deleteMessage(req, res) { - try { - const { id } = req.params; - const { _id: userId } = req.user; + try { + const { id } = req.params; + const { _id: userId } = req.user; - const userRole = req.user.role.toLowerCase(); + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } - if (!id) { - return res.status(404).json({ success: false, message: 'Message Id not found.' }); - } + if (!id) { + return res.status(404).json({ success: false, message: 'Message Id not found.' }); + } - const message = await MessagesModel.findOne({ _id: id }); + const message = await MessagesModel.findOne({ _id: id }); - if (!message) { - return res.status(404).json({ success: false, message: 'Message not found for ${id}' }); - } + if (!message) { + return res.status(404).json({ success: false, message: 'Message not found for ${id}' }); + } - if (message.createdBy.toString() !== userId.toString()) { - return res.status(401).json({ success: false, message: 'Not authorised to delete this message' }); - } + if (message.createdBy.toString() !== userId.toString()) { + return res.status(401).json({ success: false, message: 'Not authorised to delete this message' }); + } - await MessagesModel.remove({ _id: id }); + await MessagesModel.remove({ _id: id }); - const messagesCount = await MessagesModel.count({ topic: message.topic }); + const messagesCount = await MessagesModel.count({ topic: message.topic }); - if (!messagesCount) { - await TopicModel.remove({ _id: new mongoose.Types.ObjectId(message.topic) }); - } + if (!messagesCount) { + await TopicModel.remove({ _id: new mongoose.Types.ObjectId(message.topic) }); + } - return res.status(204).json({ success: true }); - } catch (err) { - process.stdout.write(`MESSAGE - deleteMessage : ${err.message}\n`); - return res.status(500).json({ message: err.message }); - } - } + return res.status(204).json({ success: true }); + } catch (err) { + process.stdout.write(`MESSAGE - deleteMessage : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } + } - async updateMessage(req, res) { - try { - let { _id: userId } = req.user; - let { messageId, isRead, messageDescription = '', messageType = '' } = req.body; + async updateMessage(req, res) { + try { + let { _id: userId } = req.user; + let { messageId, isRead, messageDescription = '', messageType = '' } = req.body; - const userRole = req.user.role.toLowerCase(); + const userRole = req.user.role.toLowerCase(); - if (!this.checkUserRole(userRole)) { - process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); - res.status(500).json(`An error occurred - user does not have the right roles`); - } + if (!this.checkUserRole(userRole)) { + process.stdout.write(`MESSAGE - createMessage : the user role is not Admin or Creator`); + res.status(500).json(`An error occurred - user does not have the right roles`); + } - if (!messageId) return res.status(404).json({ success: false, message: 'Message Id not found.' }); + if (!messageId) return res.status(404).json({ success: false, message: 'Message Id not found.' }); - const message = await MessagesModel.findOne({ _id: messageId }); + const message = await MessagesModel.findOne({ _id: messageId }); - if (!message) { - return res.status(404).json({ success: false, message: 'Message not found.' }); - } + if (!message) { + return res.status(404).json({ success: false, message: 'Message not found.' }); + } - if (isRead && !message.readBy.includes(userId.toString())) { - message.readBy.push(userId); - } + if (isRead && !message.readBy.includes(userId.toString())) { + message.readBy.push(userId); + } - if (isRead) { - message.isRead = isRead; - } + if (isRead) { + message.isRead = isRead; + } - if (!_.isEmpty(messageDescription)) { - message.messageDescription = messageDescription; - } + if (!_.isEmpty(messageDescription)) { + message.messageDescription = messageDescription; + } - if (!_.isEmpty(messageType)) { - message.messageType = messageType; - } + if (!_.isEmpty(messageType)) { + message.messageType = messageType; + } - await message.save(); + await message.save(); - return res.status(204).json({ success: true }); - } catch (err) { - process.stdout.write(`MESSAGE - updateMessage : ${err.message}\n`); - return res.status(500).json({ message: err.message }); - } + return res.status(204).json({ success: true }); + } catch (err) { + process.stdout.write(`MESSAGE - updateMessage : ${err.message}\n`); + return res.status(500).json({ message: err.message }); + } } async getDataMessages(pipeline) { From 37db0d982bf85dd293cb100573c0c7c13300b55d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 11:14:35 +0000 Subject: [PATCH 035/229] cosmetic update - alignment --- src/resources/message/v3/notification.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/message/v3/notification.js b/src/resources/message/v3/notification.js index 020a3287..49ee5e87 100644 --- a/src/resources/message/v3/notification.js +++ b/src/resources/message/v3/notification.js @@ -1,5 +1,4 @@ import _ from 'lodash'; -import mongoose from 'mongoose'; import constants from '../../utilities/constants.util'; import emailGenerator from '../../utilities/emailGenerator.util'; From df66430e8081064deced84e81be024568d07cfcf Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 11:19:44 +0000 Subject: [PATCH 036/229] cosmetic update - alignment --- src/resources/message/v3/message.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/message/v3/message.route.js b/src/resources/message/v3/message.route.js index 4031aefa..1b27d581 100644 --- a/src/resources/message/v3/message.route.js +++ b/src/resources/message/v3/message.route.js @@ -23,7 +23,7 @@ router.get('/unread/count', passport.authenticate('jwt'), (req, res) => MessageC // @access Private router.post('/', passport.authenticate('jwt'), (req, res) => MessageController.createMessage(req, res)); -// @route PUT api/messages +// @route PUT api/v3/messages // @desc PUT Update a message; user need to be Admin or Creator // @access Private router.put('/', passport.authenticate('jwt'), (req, res) => MessageController.updateMessage(req, res)); From 6a88bff9ea7633200076b9a909a0b7e7a7426bb0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 11:41:10 +0000 Subject: [PATCH 037/229] cosmetic update - alignment --- src/resources/message/v3/message.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/message/v3/message.controller.js b/src/resources/message/v3/message.controller.js index fd2f0c18..37088e07 100644 --- a/src/resources/message/v3/message.controller.js +++ b/src/resources/message/v3/message.controller.js @@ -211,7 +211,7 @@ class MessageController const message = await MessagesModel.findOne({ _id: id }); if (!message) { - return res.status(404).json({ success: false, message: 'Message not found for ${id}' }); + return res.status(404).json({ success: false, message: `Message not found for ${id}` }); } if (message.createdBy.toString() !== userId.toString()) { From 00de3b0a143b0c0ac8f9c4cfc2938e6ede6061ce Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 6 Dec 2022 15:14:38 +0000 Subject: [PATCH 038/229] update review endpoint --- src/resources/review/v3/review.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/review/v3/review.route.js b/src/resources/review/v3/review.route.js index d376b5f3..cdeee88d 100644 --- a/src/resources/review/v3/review.route.js +++ b/src/resources/review/v3/review.route.js @@ -15,6 +15,6 @@ router.get('/:reviewId?', passport.authenticate('jwt'), (req, res) => ReviewCont // @bodyParam {string} activeflag can be: active/approve (approve will be converted in active), reject, archive // @desc update active flag by reviewId // @access Private -router.patch('/:reviewId', passport.authenticate('jwt'), (req, res) => ReviewController.updateReviews(req, res)); +router.patch('/:reviewId', passport.authenticate('jwt'), (req, res) => ReviewController.updateStateReviews(req, res)); module.exports = router; From 22e0ca9ff14560a4338a108fe3f9155011498e25 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 9 Dec 2022 10:21:23 +0000 Subject: [PATCH 039/229] added dependabot --- .github/dependabot.yml | 6 ++++++ Dockerfile | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..6ec890f4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'monthly' \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f265113a..c1fc8600 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12 +FROM node:14 # Create app directory WORKDIR /usr/src/app From 24404cf16ea5c1a7737d29857780aedb2c5be38b Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Mon, 12 Dec 2022 10:57:43 +0000 Subject: [PATCH 040/229] GAT-1811: Setup Code scanning for gateway-api --- .github/workflows/codeql.yml | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..1bfaf023 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "dev", UAT, UATBeta, master, release ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "dev" ] + schedule: + - cron: '26 5 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From e8a73cb99f7c921b0d2229e2b5a882a3a726d9a4 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 11:59:28 +0000 Subject: [PATCH 041/229] update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8ef7f6e1..3afa4855 100644 --- a/README.md +++ b/README.md @@ -151,4 +151,5 @@ terraform plan -var-file=vars.tfvars -out=tf_apply terraform apply tf_apply && rm tf_apply ``` + [Link to terraform file](deployment/GCP/api.tf) From 3bc9202a9b8c09a9dc8762a8635893a150dad20f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:01:55 +0000 Subject: [PATCH 042/229] Bump plop from 2.7.6 to 3.1.1 Bumps [plop](https://github.com/plopjs/plop/tree/HEAD/packages/plop) from 2.7.6 to 3.1.1. - [Release notes](https://github.com/plopjs/plop/releases) - [Changelog](https://github.com/plopjs/plop/blob/main/packages/plop/CHANGELOG.md) - [Commits](https://github.com/plopjs/plop/commits/plop@3.1.1/packages/plop) --- updated-dependencies: - dependency-name: plop dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294ee1fd..d5b9b7c1 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "jest": "^26.6.3", "mongodb-memory-server": "6.9.2", "nodemon": "^2.0.3", - "plop": "^2.7.4", + "plop": "^3.1.1", "supertest": "^4.0.2" }, "scripts": { From 6588e48044510f5dc6ed516d542a91ea3a3adbda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:02:33 +0000 Subject: [PATCH 043/229] Bump @google-cloud/monitoring from 2.3.5 to 3.0.3 Bumps [@google-cloud/monitoring](https://github.com/googleapis/nodejs-monitoring) from 2.3.5 to 3.0.3. - [Release notes](https://github.com/googleapis/nodejs-monitoring/releases) - [Changelog](https://github.com/googleapis/nodejs-monitoring/blob/v3.0.3/CHANGELOG.md) - [Commits](https://github.com/googleapis/nodejs-monitoring/compare/v2.3.5...v3.0.3) --- updated-dependencies: - dependency-name: "@google-cloud/monitoring" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294ee1fd..c1fd9a35 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "private": true, "dependencies": { "@google-cloud/bigquery": "^5.9.3", - "@google-cloud/monitoring": "^2.1.0", + "@google-cloud/monitoring": "^3.0.3", "@google-cloud/pubsub": "^2.19.4", "@google-cloud/storage": "^5.3.0", "@hubspot/api-client": "^4.1.0", From f3f1f9dffd87ae6e8d9cff24dc45dbffa2d9efb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:03:12 +0000 Subject: [PATCH 044/229] Bump supertest from 4.0.2 to 6.3.3 Bumps [supertest](https://github.com/visionmedia/supertest) from 4.0.2 to 6.3.3. - [Release notes](https://github.com/visionmedia/supertest/releases) - [Commits](https://github.com/visionmedia/supertest/compare/v4.0.2...v6.3.3) --- updated-dependencies: - dependency-name: supertest dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294ee1fd..c6e63d52 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "mongodb-memory-server": "6.9.2", "nodemon": "^2.0.3", "plop": "^2.7.4", - "supertest": "^4.0.2" + "supertest": "^6.3.3" }, "scripts": { "start-with-migrate": "./node_modules/.bin/migrate up && node index.js", From 1a7e412dc0804deff2e4c7308352478e96801f6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 14:42:47 +0000 Subject: [PATCH 045/229] Bump @hubspot/api-client from 4.1.0 to 8.2.0 Bumps [@hubspot/api-client](https://github.com/HubSpot/hubspot-api-nodejs) from 4.1.0 to 8.2.0. - [Release notes](https://github.com/HubSpot/hubspot-api-nodejs/releases) - [Changelog](https://github.com/HubSpot/hubspot-api-nodejs/blob/master/CHANGELOG.md) - [Commits](https://github.com/HubSpot/hubspot-api-nodejs/commits) --- updated-dependencies: - dependency-name: "@hubspot/api-client" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 663f79e7..88e9bcc5 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@google-cloud/monitoring": "^3.0.3", "@google-cloud/pubsub": "^2.19.4", "@google-cloud/storage": "^5.3.0", - "@hubspot/api-client": "^4.1.0", + "@hubspot/api-client": "^8.2.0", "@sendgrid/mail": "^7.1.0", "ajv": "^8.1.0", "ajv-formats": "^2.0.2", From c5d88ef1d4c3bb0c915293ba7276574152d6cf98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:06:26 +0000 Subject: [PATCH 046/229] Bump sinon from 9.2.4 to 15.0.0 Bumps [sinon](https://github.com/sinonjs/sinon) from 9.2.4 to 15.0.0. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md) - [Commits](https://github.com/sinonjs/sinon/compare/v9.2.4...v15.0.0) --- updated-dependencies: - dependency-name: sinon dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 88e9bcc5..ef59cfdd 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "randomstring": "^1.1.5", "redis": "4.0.0", "simple-gcp-logging": "git+https://github.com/HDRUK/simple-gcp-logging.git#main", - "sinon": "^9.2.4", + "sinon": "^15.0.0", "snyk": "^1.334.0", "swagger-ui-express": "^4.1.4", "test": "^0.6.0", From 292cc3b44cdd346d781d027437244d4d54a97c01 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 15:57:39 +0000 Subject: [PATCH 047/229] remove exposed port from docker file --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c1fc8600..99d40349 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,5 +15,5 @@ RUN npm install --production # Bundle app source COPY . . -EXPOSE 3001 +# EXPOSE 3001 CMD [ "node", "index.js" ] From 17b12dce97b21bbd3638588b8b4446f0813cda2f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 16:10:33 +0000 Subject: [PATCH 048/229] rollback hubspot npm --- package.json | 2 +- src/config/server.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ef59cfdd..12132168 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@google-cloud/monitoring": "^3.0.3", "@google-cloud/pubsub": "^2.19.4", "@google-cloud/storage": "^5.3.0", - "@hubspot/api-client": "^8.2.0", + "@hubspot/api-client": "^4.1.0", "@sendgrid/mail": "^7.1.0", "ajv": "^8.1.0", "ajv-formats": "^2.0.2", diff --git a/src/config/server.js b/src/config/server.js index 60b5c46f..99a649a0 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -18,7 +18,7 @@ var app = express(); const Account = require('./account'); const configuration = require('./configuration'); -const API_PORT = process.env.PORT || 3001; +const API_PORT = process.env.PORT || 8080; const session = require('express-session'); app.disable('x-powered-by'); From 803a9ec2051976d95777ba5fa7ad9afedf57d4b6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 16:11:15 +0000 Subject: [PATCH 049/229] rollback hubspot npm --- src/config/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/server.js b/src/config/server.js index 99a649a0..60b5c46f 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -18,7 +18,7 @@ var app = express(); const Account = require('./account'); const configuration = require('./configuration'); -const API_PORT = process.env.PORT || 8080; +const API_PORT = process.env.PORT || 3001; const session = require('express-session'); app.disable('x-powered-by'); From c4e05cb42f692460ab96fe4c87ee912d64f24302 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 16:20:35 +0000 Subject: [PATCH 050/229] enable expose 3001 in dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 99d40349..c1fc8600 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,5 +15,5 @@ RUN npm install --production # Bundle app source COPY . . -# EXPOSE 3001 +EXPOSE 3001 CMD [ "node", "index.js" ] From 58879fa8681037ee10e499225bdaafe1f38f619c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 16:29:29 +0000 Subject: [PATCH 051/229] remove automatic migrate --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 12132168..4cb96b4d 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "supertest": "^6.3.3" }, "scripts": { - "start-with-migrate": "./node_modules/.bin/migrate up && node index.js", "start": "node index.js", "server": "nodemon --ignore 'src/**/*.json' index.js", "debug": "nodemon --inspect=0.0.0.0:3001 index.js", From d64af56c089facbfd2c05def17106edc8e6d1569 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 12 Dec 2022 16:35:13 +0000 Subject: [PATCH 052/229] remove automatic migrate --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4cb96b4d..12132168 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "supertest": "^6.3.3" }, "scripts": { + "start-with-migrate": "./node_modules/.bin/migrate up && node index.js", "start": "node index.js", "server": "nodemon --ignore 'src/**/*.json' index.js", "debug": "nodemon --inspect=0.0.0.0:3001 index.js", From 3c8b7f7f8325baa7a5161167dda3cb075b4bc173 Mon Sep 17 00:00:00 2001 From: Loki Date: Thu, 15 Dec 2022 17:29:48 +0000 Subject: [PATCH 053/229] adds new scoring endpoint to use existing functionality but abstract into outside usage --- src/config/server.js | 2 ++ src/resources/metadata/metadata.route.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/resources/metadata/metadata.route.js diff --git a/src/config/server.js b/src/config/server.js index 60b5c46f..57dcc6aa 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -236,6 +236,8 @@ app.use('/api/v2/questionbank', require('../resources/questionbank/questionbank. app.use('/api/v2/data-use-registers', require('../resources/dataUseRegister/dataUseRegister.route')); app.use('/api/v1/locations', require('../resources/spatialfilter/SpatialRouter')); +app.use('/api/v1/metadata', require('../resources/metadata/metadata.route')); + initialiseAuthentication(app); // launch our backend into a port diff --git a/src/resources/metadata/metadata.route.js b/src/resources/metadata/metadata.route.js new file mode 100644 index 00000000..cc9e4596 --- /dev/null +++ b/src/resources/metadata/metadata.route.js @@ -0,0 +1,22 @@ +import express from 'express'; +import passport from 'passport'; + +import { utils } from '../auth'; +import { ROLES } from '../user/user.roles'; + +import datasetonboardingUtil from '../../utils/datasetonboarding.util'; + +const router = express.Router({ mergeParams: true }); + +router.post('/scoring', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin), async (req, res) => { + const { dataset } = req.body; + + if (!dataset) { + res.json({ success: false, error: 'Dataset object must be supplied and contain all required data', status: 400 }); + } + + const verdict = await datasetonboardingUtil.buildMetadataQuality(dataset, dataset.datasetv2, dataset.pid); + res.json({ success: true, data: verdict, status: 200 }); +}); + +module.exports = router; \ No newline at end of file From 78145dd9e97a89ed40b5fa5f0269b63de627ca4c Mon Sep 17 00:00:00 2001 From: Loki Date: Fri, 16 Dec 2022 16:55:03 +0000 Subject: [PATCH 054/229] critical exploit fixed after scanning --- src/utils/datasetonboarding.util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 139e79c7..b151df00 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -758,7 +758,7 @@ const buildMetadataQuality = async (dataset, v2Object, pid) => { let rawdata = fs.readFileSync(__dirname + '/schema.json'); schema = JSON.parse(rawdata); - const ajv = new Ajv({ strict: false, allErrors: true }); + const ajv = new Ajv({ strict: false, allErrors: false }); addFormats(ajv); const validate = ajv.compile(schema); validate(cleanV2Object); From 6d54e67269f5da2af2414c625214e9112ef04110 Mon Sep 17 00:00:00 2001 From: Geeta Date: Mon, 19 Dec 2022 16:51:56 +0000 Subject: [PATCH 055/229] changed folder name from 'latest' to '2.1' --- src/resources/dataset/dataset.entity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/dataset/dataset.entity.js b/src/resources/dataset/dataset.entity.js index 2edc0815..8516dbad 100644 --- a/src/resources/dataset/dataset.entity.js +++ b/src/resources/dataset/dataset.entity.js @@ -50,7 +50,7 @@ export default class DatasetClass extends Entity { transformedObject.dataset['@schema'] = { type: `Dataset`, version: `2.0.0`, - url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/latest/dataset.schema.json`, + url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/2.1/dataset.schema.json`, } // Return v2 object From 12bb029d993badf6c6c5ac1b42613bf150c1e410 Mon Sep 17 00:00:00 2001 From: Loki Sinclair Date: Wed, 11 Jan 2023 10:32:51 +0000 Subject: [PATCH 056/229] to fool github into recognising the file move --- .old.migrations/1620418612003-test_migration.js | 1 + .old.migrations/1631268706696-shared_applications_versioning.js | 1 + 2 files changed, 2 insertions(+) diff --git a/.old.migrations/1620418612003-test_migration.js b/.old.migrations/1620418612003-test_migration.js index fa2441b4..0f0ed954 100644 --- a/.old.migrations/1620418612003-test_migration.js +++ b/.old.migrations/1620418612003-test_migration.js @@ -1,4 +1,5 @@ //import { UserModel } from '../src/resources/user/user.model'; +// Something /** * Make any changes you need to make to the database here diff --git a/.old.migrations/1631268706696-shared_applications_versioning.js b/.old.migrations/1631268706696-shared_applications_versioning.js index 19119202..e3eca7a2 100644 --- a/.old.migrations/1631268706696-shared_applications_versioning.js +++ b/.old.migrations/1631268706696-shared_applications_versioning.js @@ -1,4 +1,5 @@ import { DataRequestModel } from '../src/resources/datarequest/datarequest.model'; +// Something else async function up() { // 1. Add default application type to all applications From b001ba7b7d3be17b2efcf9c9707e4923c3cb4381 Mon Sep 17 00:00:00 2001 From: Geeta Date: Wed, 11 Jan 2023 14:46:36 +0000 Subject: [PATCH 057/229] updated auth error handling for Azure --- src/resources/auth/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 380d9243..231e6543 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -129,7 +129,7 @@ const getTeams = async () => { const catchLoginErrorAndRedirect = (req, res, next) => { if (req.auth.err || !req.auth.user) { - if (req.auth.err === 'loginError') { + if (req.auth.err === 'loginError' || req.auth.user === undefined) { return res.status(200).redirect(process.env.homeURL + '/loginerror'); } From a2bff4299c2554f119304b5080a7fb241f260440 Mon Sep 17 00:00:00 2001 From: Geeta Date: Fri, 13 Jan 2023 14:39:56 +0000 Subject: [PATCH 058/229] changed '2.1' to '2.1.0' --- src/resources/dataset/dataset.entity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/dataset/dataset.entity.js b/src/resources/dataset/dataset.entity.js index 8516dbad..2e00900d 100644 --- a/src/resources/dataset/dataset.entity.js +++ b/src/resources/dataset/dataset.entity.js @@ -50,7 +50,7 @@ export default class DatasetClass extends Entity { transformedObject.dataset['@schema'] = { type: `Dataset`, version: `2.0.0`, - url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/2.1/dataset.schema.json`, + url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/2.1.0/dataset.schema.json`, } // Return v2 object From 0152b6e12c9c1cedf04d4117616712d69d5fd35e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 19 Jan 2023 17:31:30 +0000 Subject: [PATCH 059/229] get members from team by teamid --- Dockerfile.dev | 4 +- src/config/server.js | 6 + src/middlewares/errorHandler.middleware.js | 24 + src/middlewares/index.js | 2 + src/resources/team/v3/team.controller.js | 36 + src/resources/team/v3/team.route.js | 13 + src/resources/team/v3/team.service.js | 26 + .../utilities/__mocks__/checkIfAdmin.mock.js | 1274 +++++++++++++++++ .../__mocks__/checkTeamV3Permissions.mock.js | 380 +++++ .../__mocks__/formatTeamMembers.mock.js | 441 ++++++ .../utilities/__tests__/checkIfAdmin.test.js | 18 + .../__tests__/checkTeamV3Permissions.test.js | 32 + .../__tests__/formatTeamMembers.test.js | 14 + src/resources/utilities/team.v3.util.js | 93 ++ 14 files changed, 2361 insertions(+), 2 deletions(-) create mode 100644 src/middlewares/errorHandler.middleware.js create mode 100644 src/resources/team/v3/team.controller.js create mode 100644 src/resources/team/v3/team.route.js create mode 100644 src/resources/team/v3/team.service.js create mode 100644 src/resources/utilities/__mocks__/checkIfAdmin.mock.js create mode 100644 src/resources/utilities/__mocks__/checkTeamV3Permissions.mock.js create mode 100644 src/resources/utilities/__mocks__/formatTeamMembers.mock.js create mode 100644 src/resources/utilities/__tests__/checkIfAdmin.test.js create mode 100644 src/resources/utilities/__tests__/checkTeamV3Permissions.test.js create mode 100644 src/resources/utilities/__tests__/formatTeamMembers.test.js create mode 100644 src/resources/utilities/team.v3.util.js diff --git a/Dockerfile.dev b/Dockerfile.dev index a9f1591d..f0ad3803 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:14 +FROM node:16 ENV GOOGLE_APPLICATION_CREDENTIALS="/usr/local/etc/gcloud/application_default_credentials.json" @@ -12,4 +12,4 @@ COPY . . EXPOSE 3001 -CMD ["npm", "run", "server"] \ No newline at end of file +CMD ["npm", "run", "server"] \ No newline at end of file diff --git a/src/config/server.js b/src/config/server.js index cbdb1bbd..e3bfeb05 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -14,6 +14,8 @@ import * as Sentry from '@sentry/node'; import * as Tracing from '@sentry/tracing'; import helper from '../resources/utilities/helper.util'; +import { errorHandler } from '../middlewares'; + require('dotenv').config(); var app = express(); @@ -191,7 +193,10 @@ app.use('/api/v1/auth/register', require('../resources/user/user.register.route' app.use('/api/v1/users', require('../resources/user/user.route')); app.use('/api/v1/topics', require('../resources/topic/topic.route')); app.use('/api/v1/publishers', require('../resources/publisher/publisher.route')); + app.use('/api/v1/teams', require('../resources/team/team.route')); +app.use('/api/v3/teams', require('../resources/team/v3/team.route')); + app.use('/api/v1/workflows', require('../resources/workflow/workflow.route')); app.use('/api/v1/messages', require('../resources/message/message.route')); app.use('/api/v1/reviews', require('../resources/tool/review.route')); @@ -258,6 +263,7 @@ app.use('/api/v2/questionbank', require('../resources/questionbank/questionbank. app.use('/api/v2/data-use-registers', require('../resources/dataUseRegister/dataUseRegister.route')); app.use('/api/v1/locations', require('../resources/spatialfilter/SpatialRouter')); +app.use(errorHandler); initialiseAuthentication(app); // launch our backend into a port diff --git a/src/middlewares/errorHandler.middleware.js b/src/middlewares/errorHandler.middleware.js new file mode 100644 index 00000000..9c54aabe --- /dev/null +++ b/src/middlewares/errorHandler.middleware.js @@ -0,0 +1,24 @@ + +import { LoggingService } from "../services"; + +const errorHandler = (error, req, res, next) => { + const errorStatusCode = error.status || 500; + const loggingService = new LoggingService(); + const loggingEnabled = parseInt(process.env.LOGGING_LOG_ENABLED) || 0; + + const errorMessage = { + type: 'error', + message: error.message, + stack: error.stack.split("\n"), + }; + + if (loggingEnabled) { + loggingService.sendDataInLogging(errorMessage, 'ERROR'); + } + + res.status(errorStatusCode).json(errorMessage); + + return; +} + +export { errorHandler } \ No newline at end of file diff --git a/src/middlewares/index.js b/src/middlewares/index.js index 74cdab05..6addd471 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -12,6 +12,7 @@ import checkInputMiddleware from './checkInputMiddleware'; import checkMinLengthMiddleware from './checkMinLengthMiddleware'; import checkStringMiddleware from './checkStringMiddleware'; import { validateUpdateRequest, validateUploadRequest, authorizeUpdate, authorizeUpload } from './dataUseRegister.middleware'; +import { errorHandler } from './errorHandler.middleware'; export { checkIDMiddleware, @@ -30,4 +31,5 @@ export { validateUploadRequest, authorizeUpdate, authorizeUpload, + errorHandler, }; diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js new file mode 100644 index 00000000..1612917d --- /dev/null +++ b/src/resources/team/v3/team.controller.js @@ -0,0 +1,36 @@ +// import { getDBTeamMembers } from './team.database'; + +import TeamService from './team.service'; +import teamV3Util from '../../utilities/team.v3.util'; +import constants from '../../utilities/constants.util'; + +class TeamController extends TeamService { + constructor() { + super(); + } + + async getTeamMembers(req, res) { + const teamId = req.params.teamid; + const user = req.user; + const currentUserId = req.user._id; + + const team = await this.getMembersByTeamId(teamId); + + let authorised = teamV3Util.checkTeamV3Permissions('', team.toObject(), currentUserId); + if (!authorised) { + authorised = teamV3Util.checkIfAdmin(user, [constants.roleTypes.ADMIN_DATASET]); + } + if (!authorised) { + return res.status(401).json({ success: false }); + } + + let members = teamV3Util.formatTeamMembers(team); +console.log(members); + res.status(200).json({ + members, + }); + } + +} + +module.exports = new TeamController(); \ No newline at end of file diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js new file mode 100644 index 00000000..9eca7c81 --- /dev/null +++ b/src/resources/team/v3/team.route.js @@ -0,0 +1,13 @@ +import express from 'express'; +import passport from 'passport'; + +const TeamController = require('./team.controller'); + +const router = express.Router(); + +// @route GET api/v3/teams/:teamid/members +// @desc GET all team members for team +// @access Private +router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.getTeamMembers(req, res)); + +module.exports = router; \ No newline at end of file diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js new file mode 100644 index 00000000..5d8db47a --- /dev/null +++ b/src/resources/team/v3/team.service.js @@ -0,0 +1,26 @@ +import { TeamModel } from "../team.model"; + +export default class TeamService { + constructor() {} + + async getMembersByTeamId(teamId) { + try { + const team = await TeamModel.findOne({ _id: teamId }).populate({ + path: 'users', + populate: { + path: 'additionalInfo', + select: 'organisation bio showOrganisation showBio news', + }, + }); + + if (!team) { + throw new Error(`Team not Found`); + } + + return team; + } catch (e) { + process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); + throw new Error(e.message); + } + } +} diff --git a/src/resources/utilities/__mocks__/checkIfAdmin.mock.js b/src/resources/utilities/__mocks__/checkIfAdmin.mock.js new file mode 100644 index 00000000..ea923d91 --- /dev/null +++ b/src/resources/utilities/__mocks__/checkIfAdmin.mock.js @@ -0,0 +1,1274 @@ +const mockUser = { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "61f91d232e175937b960e213", + "id": 42412943236984630, + "providerId": "100742128864395249791", + "provider": "google", + "firstname": "Dan", + "lastname": "Nita", + "email": "dan.nita.hdruk@gmail.com", + "role": "Admin", + "createdAt": "2022-02-01T11:44:35.584Z", + "updatedAt": "2022-11-29T10:26:06.318Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "12e3760a22942a100ea08036b93837a01cf615988fc7d312e50d733d9367d861", + "discourseUsername": "dan.nita", + "teams": [ + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "62837ba8a35a2205b0f0a0b6", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "628cc003cc879f3d43852b8f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f8992a97150a1b050be0712", + "name": "ALLIANCE > PUBLIC HEALTH SCOTLAND" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623467f6ce42aab1cfc29021", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f3f98068af2ef61552e1d75", + "name": "ALLIANCE > SAIL" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61128e7ef7ff9cee652532b4", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "623c81f5aa033e0e643d6c6a", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [ + { + "optIn": true, + "_id": "62c40c6ab1079d7d4ee1c608", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61b9c46dfbd7e9f3aa270ac1" + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6193e832536f42aa8f976fdc", + "notifications": [ + { + "optIn": true, + "_id": "623c515eaa033e6d0d3d6473", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "62839a8883f55d40d3d20cf4", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62ebe58bd7595507ba81d689", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62ed1f7e7d1af36e6d280b8e", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ef9c4ebb9796854174cbf94", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61dd491e4d274e7eac3b848e", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f7b1a2bce9f65e6ed83e7da", + "name": "OTHER > HEALTH DATA RESEARCH UK" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5f8563c6fa9a256698a9fafb", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61854c1e46f52ed51754bb24" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "60191005c13ca825e4c6cadd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f89662f7150a1b050be0710", + "name": "ALLIANCE > HEALTH AND SOCIAL CARE NORTHERN IRELAND" + } + }, + { + "members": [ + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e725692c9a581a0dd2bd84b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e726049c9a58131cd2bd874", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6167edbd5306ac30d5b1da14", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619261978f832546e0b0edfd", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619501876b8724ad01077af6", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61978834a87688d4e67770fa", + "notifications": [] + }, + { + "roles": [ + "admin_data_use" + ], + "memberid": "619ba1e87d2fd8db23885ce9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61b9c46dfbd7e9f3aa270ac1", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61854c1e46f52ed51754bb24", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62349a67767db5d3408b4001", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61e93915826d995980cca3dc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "626a72ba4524adcf224b769b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + } + ], + "type": "admin", + "publisher": null + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62f2335e062fcd05b9e8d2cf", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62f241e8f15b29ce1c24fd96", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62c80ec7970e8a07797c23c7", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "6318aab2f051c6375ec53b7e", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "628f9e65b089fa694655d168", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "5fc8e2b7c386587231140f85", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5f7c2bcd504d5e7cda30b3ea", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5e851759b9bbd5ecd9f65a39", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "634c518998e119341680d558", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "61e57fd3012bda94e0e8b9c6", + "name": "OTHER > Priti Test Team" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [ + { + "optIn": true, + "_id": "62389e82e5c72465f718013f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61cc3966928507fe0f1b9325", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "6255a87db588c01f5250a3ce", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "62024195f0e15612a4e16979", + "name": "OTHER > Test1" + } + } + ] +}; + +const mockRole = [ + "admin_dataset" +]; +const mockRoleEmpty = []; + +export { + mockUser, + mockRole, + mockRoleEmpty, +} \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/checkTeamV3Permissions.mock.js b/src/resources/utilities/__mocks__/checkTeamV3Permissions.mock.js new file mode 100644 index 00000000..33253471 --- /dev/null +++ b/src/resources/utilities/__mocks__/checkTeamV3Permissions.mock.js @@ -0,0 +1,380 @@ +const mockRole = ""; +const mockRoleManager = "manager"; + +const mockTeam = { + "active": true, + "_id": "619658d8a87688acd2775721", + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6167edbd5306ac30d5b1da14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61a519eb06447b0e9b6ae3fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + } + ], + "notifications": [], + "type": "publisher", + "createdAt": "2021-11-18T13:44:56.935Z", + "updatedAt": "2022-07-22T09:14:00.954Z", + "__v": 3, + "users": [ + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "6167edbd5306ac30d5b1da14", + "id": 3201332329402178, + "providerId": "113237134008791776027", + "provider": "google", + "firstname": "Varsha", + "lastname": "Khodiyar", + "email": "varsha.khodiyar@hdruk.ac.uk", + "role": "Creator", + "createdAt": "2021-10-14T08:43:41.185Z", + "updatedAt": "2021-10-14T08:44:06.191Z", + "__v": 0, + "redirectURL": "/search?search=&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "90f50e560155b1ffac50927b25dea45648babda0ef34ab53bc09bd0ec9ca55f4", + "discourseUsername": "varsha.khodiyar", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 3201332329402178, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "Health Data Research UK", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "61a519eb06447b0e9b6ae3fd", + "id": 4539054910544198, + "providerId": "660a0c9a-e529-4785-9b00-f46ea0f8ffb7", + "provider": "azure", + "firstname": "Louis", + "lastname": "Grandjean", + "role": "Creator", + "createdAt": "2021-11-29T18:20:27.801Z", + "updatedAt": "2021-11-29T19:28:24.512Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "a069e40d8d254c7730fb9a273a8d3e81c8d7c5509bce4ced6644442bf7baa203", + "discourseUsername": "louis.grandjean", + "email": "Louis.grandjean@gosh.nhs.uk", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 4539054910544198, + "bio": "Associate Professor of Infectious Diseases at UCL and Honorary Consultant Paediatric Infectious Diseases at Great Ormond Street Hospital", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "61825367cefce1bfe5c9ba7c", + "id": 1214329286003002, + "providerId": "103353525483349097009", + "provider": "google", + "firstname": "Priti", + "lastname": "Pampatwar", + "email": "priti.pampatwar@hdruk.ac.uk", + "role": "Admin", + "createdAt": "2021-11-03T09:16:23.610Z", + "updatedAt": "2021-11-08T13:15:12.514Z", + "__v": 0, + "redirectURL": "/search?search=&loginReferrer=http%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "8ebd0df738d8027c90ed7b7b2d88e6329aaeb708d26a7a7ec5bfdfdc9830f104", + "discourseUsername": "priti.pampatwar1", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 1214329286003002, + "bio": "Need access to UAT to explore Gateway features for analysis. thanks", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "HDRUK", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "623483baff441ae7fec9fd43", + "id": 5992307808590300, + "providerId": "110650484151346440783", + "provider": "google", + "firstname": "Hdr", + "lastname": "GatewayAdmin", + "email": "hdrgatea@gmail.com", + "role": "Admin", + "createdAt": "2022-03-18T13:06:02.463Z", + "updatedAt": "2022-11-30T14:21:04.554Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "1a487eb79808d4037497e999794bf43787a6ab4c2ae8691e7ed86be388aeb57e", + "discourseUsername": "hdr.gatewayadmin", + "acceptedAdvancedSearchTerms": true, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 5992307808590300, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "62d945d9fb5b536d1520c618", + "id": 9829759310154118, + "providerId": "103891864579295967212", + "provider": "google", + "firstname": "Yemi", + "lastname": "Aiyeola", + "email": "yemiayat@gmail.com", + "role": "Creator", + "createdAt": "2022-07-21T12:26:01.756Z", + "updatedAt": "2022-07-22T06:51:10.112Z", + "__v": 0, + "redirectURL": "/completeRegistration/9829759310154118", + "discourseKey": "", + "discourseUsername": null, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 9829759310154118, + "bio": "Test account", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "HDR UK", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "5ec2a116b293e07eb48afe14", + "id": 45222846999444660, + "providerId": "111308516852033336265", + "provider": "google", + "firstname": "Clara", + "lastname": "Fennessy", + "email": "clara.fennessy@hdruk.ac.uk", + "role": "Admin", + "__v": 0, + "redirectURL": "/search?search=COVID-19&type=all&tab=Projects&toolcategory=&programminglanguage=&features=&topics=&license=&sampleavailability=&keywords=&publisher=&ageband=&geographiccover=", + "updatedAt": "2022-01-17T10:03:53.640Z", + "createdAt": "2020-09-04T00:00:00.000Z", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 45222846999444660, + "bio": "HDR UK", + "link": "", + "orcid": "", + "activeflag": "active", + "organisation": "", + "showBio": true, + "terms": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "62384f08e5c7245adf17f0fd", + "id": 6644721675822300, + "providerId": "116978061621110601234", + "provider": "google", + "firstname": "Vijayalakshmi", + "lastname": "Shanmugam", + "email": "vijisrisan@gmail.com", + "role": "Creator", + "createdAt": "2022-03-21T10:10:16.388Z", + "updatedAt": "2022-11-15T11:31:21.426Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&tab=Datasets", + "discourseKey": "", + "discourseUsername": null, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 6644721675822300, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "62028ae4d62405c442fd383f", + "id": 37447512737860000, + "providerId": "108928523103518483536", + "provider": "google", + "firstname": "Gateway", + "lastname": "Custodian", + "email": "custodianhdr01@gmail.com", + "role": "Admin", + "createdAt": "2022-02-08T15:23:16.406Z", + "updatedAt": "2022-11-30T13:47:30.946Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "8860b4f92315a4b54ee1f9fed1078e88d119a9260b27b2d33b7995003d3e792a", + "discourseUsername": "gateway.custodian", + "acceptedAdvancedSearchTerms": true, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 37447512737860000, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + } + ] +}; + +const mockTeamEmpty = {}; +const mockUserIdNotInList = "61f91d232e175937b960e213"; +const mockUserIdInList = "6167edbd5306ac30d5b1da14"; +const mockUserIdInListNoManager = "62028ae4d62405c442fd383f"; + + + export { + mockRole, + mockRoleManager, + mockTeam, + mockTeamEmpty, + mockUserIdNotInList, + mockUserIdInList, + mockUserIdInListNoManager, + } \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/formatTeamMembers.mock.js b/src/resources/utilities/__mocks__/formatTeamMembers.mock.js new file mode 100644 index 00000000..0d0416eb --- /dev/null +++ b/src/resources/utilities/__mocks__/formatTeamMembers.mock.js @@ -0,0 +1,441 @@ +const mockTeam = { + "active": true, + "_id": "619658d8a87688acd2775721", + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6167edbd5306ac30d5b1da14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61a519eb06447b0e9b6ae3fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + } + ], + "notifications": [], + "type": "publisher", + "createdAt": "2021-11-18T13:44:56.935Z", + "updatedAt": "2022-07-22T09:14:00.954Z", + "__v": 3, + "users": [ + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "6167edbd5306ac30d5b1da14", + "id": 3201332329402178, + "providerId": "113237134008791776027", + "provider": "google", + "firstname": "Varsha", + "lastname": "Khodiyar", + "email": "varsha.khodiyar@hdruk.ac.uk", + "role": "Creator", + "createdAt": "2021-10-14T08:43:41.185Z", + "updatedAt": "2021-10-14T08:44:06.191Z", + "__v": 0, + "redirectURL": "/search?search=&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "90f50e560155b1ffac50927b25dea45648babda0ef34ab53bc09bd0ec9ca55f4", + "discourseUsername": "varsha.khodiyar", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 3201332329402178, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "Health Data Research UK", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "61a519eb06447b0e9b6ae3fd", + "id": 4539054910544198, + "providerId": "660a0c9a-e529-4785-9b00-f46ea0f8ffb7", + "provider": "azure", + "firstname": "Louis", + "lastname": "Grandjean", + "role": "Creator", + "createdAt": "2021-11-29T18:20:27.801Z", + "updatedAt": "2021-11-29T19:28:24.512Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "a069e40d8d254c7730fb9a273a8d3e81c8d7c5509bce4ced6644442bf7baa203", + "discourseUsername": "louis.grandjean", + "email": "Louis.grandjean@gosh.nhs.uk", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 4539054910544198, + "bio": "Associate Professor of Infectious Diseases at UCL and Honorary Consultant Paediatric Infectious Diseases at Great Ormond Street Hospital", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "61825367cefce1bfe5c9ba7c", + "id": 1214329286003002, + "providerId": "103353525483349097009", + "provider": "google", + "firstname": "Priti", + "lastname": "Pampatwar", + "email": "priti.pampatwar@hdruk.ac.uk", + "role": "Admin", + "createdAt": "2021-11-03T09:16:23.610Z", + "updatedAt": "2021-11-08T13:15:12.514Z", + "__v": 0, + "redirectURL": "/search?search=&loginReferrer=http%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "8ebd0df738d8027c90ed7b7b2d88e6329aaeb708d26a7a7ec5bfdfdc9830f104", + "discourseUsername": "priti.pampatwar1", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 1214329286003002, + "bio": "Need access to UAT to explore Gateway features for analysis. thanks", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "HDRUK", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "623483baff441ae7fec9fd43", + "id": 5992307808590300, + "providerId": "110650484151346440783", + "provider": "google", + "firstname": "Hdr", + "lastname": "GatewayAdmin", + "email": "hdrgatea@gmail.com", + "role": "Admin", + "createdAt": "2022-03-18T13:06:02.463Z", + "updatedAt": "2022-11-30T14:21:04.554Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "1a487eb79808d4037497e999794bf43787a6ab4c2ae8691e7ed86be388aeb57e", + "discourseUsername": "hdr.gatewayadmin", + "acceptedAdvancedSearchTerms": true, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 5992307808590300, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "62d945d9fb5b536d1520c618", + "id": 9829759310154118, + "providerId": "103891864579295967212", + "provider": "google", + "firstname": "Yemi", + "lastname": "Aiyeola", + "email": "yemiayat@gmail.com", + "role": "Creator", + "createdAt": "2022-07-21T12:26:01.756Z", + "updatedAt": "2022-07-22T06:51:10.112Z", + "__v": 0, + "redirectURL": "/completeRegistration/9829759310154118", + "discourseKey": "", + "discourseUsername": null, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 9829759310154118, + "bio": "Test account", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "HDR UK", + "showBio": true + } + }, + { + "feedback": true, + "news": true, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "5ec2a116b293e07eb48afe14", + "id": 45222846999444660, + "providerId": "111308516852033336265", + "provider": "google", + "firstname": "Clara", + "lastname": "Fennessy", + "email": "clara.fennessy@hdruk.ac.uk", + "role": "Admin", + "__v": 0, + "redirectURL": "/search?search=COVID-19&type=all&tab=Projects&toolcategory=&programminglanguage=&features=&topics=&license=&sampleavailability=&keywords=&publisher=&ageband=&geographiccover=", + "updatedAt": "2022-01-17T10:03:53.640Z", + "createdAt": "2020-09-04T00:00:00.000Z", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 45222846999444660, + "bio": "HDR UK", + "link": "", + "orcid": "", + "activeflag": "active", + "organisation": "", + "showBio": true, + "terms": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "62384f08e5c7245adf17f0fd", + "id": 6644721675822300, + "providerId": "116978061621110601234", + "provider": "google", + "firstname": "Vijayalakshmi", + "lastname": "Shanmugam", + "email": "vijisrisan@gmail.com", + "role": "Creator", + "createdAt": "2022-03-21T10:10:16.388Z", + "updatedAt": "2022-11-15T11:31:21.426Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&tab=Datasets", + "discourseKey": "", + "discourseUsername": null, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 6644721675822300, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [ + "GENERAL_ACCESS", + "SYSTEM_ADMIN" + ], + "_id": "62028ae4d62405c442fd383f", + "id": 37447512737860000, + "providerId": "108928523103518483536", + "provider": "google", + "firstname": "Gateway", + "lastname": "Custodian", + "email": "custodianhdr01@gmail.com", + "role": "Admin", + "createdAt": "2022-02-08T15:23:16.406Z", + "updatedAt": "2022-11-30T13:47:30.946Z", + "__v": 0, + "redirectURL": "/search?search=&datasetSort=latest&loginReferrer=https%3A%2F%2Fuat.healthdatagateway.org%2F&tab=Datasets", + "discourseKey": "8860b4f92315a4b54ee1f9fed1078e88d119a9260b27b2d33b7995003d3e792a", + "discourseUsername": "gateway.custodian", + "acceptedAdvancedSearchTerms": true, + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 37447512737860000, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "", + "showBio": true + } + } + ] +}; +const mockResponse = [ + { + firstname: 'Varsha', + lastname: 'Khodiyar', + userId: '6167edbd5306ac30d5b1da14', + email: 'varsha.khodiyar@hdruk.ac.uk', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: 'Health Data Research UK', + bio: '' + }, + { + firstname: 'Louis', + lastname: 'Grandjean', + userId: '61a519eb06447b0e9b6ae3fd', + email: 'Louis.grandjean@gosh.nhs.uk', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: '', + bio: 'Associate Professor of Infectious Diseases at UCL and Honorary Consultant Paediatric Infectious Diseases at Great Ormond Street Hospital' + }, + { + firstname: 'Priti', + lastname: 'Pampatwar', + userId: '61825367cefce1bfe5c9ba7c', + email: 'priti.pampatwar@hdruk.ac.uk', + roles: ["manager","metadata_editor","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: 'HDRUK', + bio: 'Need access to UAT to explore Gateway features for analysis. thanks' + }, + { + firstname: 'Hdr', + lastname: 'GatewayAdmin', + userId: '623483baff441ae7fec9fd43', + email: 'hdrgatea@gmail.com', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: '', + bio: '' + }, + { + firstname: 'Yemi', + lastname: 'Aiyeola', + userId: '62d945d9fb5b536d1520c618', + email: 'yemiayat@gmail.com', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: 'HDR UK', + bio: 'Test account' + }, + { + firstname: 'Clara', + lastname: 'Fennessy', + userId: '5ec2a116b293e07eb48afe14', + email: 'clara.fennessy@hdruk.ac.uk', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: '', + bio: 'HDR UK' + }, + { + firstname: 'Vijayalakshmi', + lastname: 'Shanmugam', + userId: '62384f08e5c7245adf17f0fd', + email: 'vijisrisan@gmail.com', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: '', + bio: '' + }, + { + firstname: 'Gateway', + lastname: 'Custodian', + userId: '62028ae4d62405c442fd383f', + email: 'custodianhdr01@gmail.com', + roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], + organisation: '', + bio: '' + } +]; + +export { + mockTeam, + mockResponse, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkIfAdmin.test.js b/src/resources/utilities/__tests__/checkIfAdmin.test.js new file mode 100644 index 00000000..e4cd7d8f --- /dev/null +++ b/src/resources/utilities/__tests__/checkIfAdmin.test.js @@ -0,0 +1,18 @@ +import teamV3Util from '../team.v3.util'; +import { + mockUser, + mockRole, + mockRoleEmpty +} from '../__mocks__/checkIfAdmin.mock'; + +describe("test checkIfAdmin", () => { + it("should return true", () => { + let response = teamV3Util.checkIfAdmin(mockUser, mockRole); + expect(response).toBe(true); + }); + + it("should return false", () => { + let response = teamV3Util.checkIfAdmin(mockUser, mockRoleEmpty); + expect(response).toBe(false); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkTeamV3Permissions.test.js b/src/resources/utilities/__tests__/checkTeamV3Permissions.test.js new file mode 100644 index 00000000..61d68cb1 --- /dev/null +++ b/src/resources/utilities/__tests__/checkTeamV3Permissions.test.js @@ -0,0 +1,32 @@ +import teamV3Util from '../team.v3.util'; +import { + mockRole, + mockRoleManager, + mockTeam, + mockTeamEmpty, + mockUserIdNotInList, + mockUserIdInList, + mockUserIdInListNoManager +} from '../__mocks__/checkTeamV3Permissions.mock'; + +describe("test for checkTeamV3Permissions", () => { + it("checkTeamV3Permissions with userid in team should return true", () => { + let response = teamV3Util.checkTeamV3Permissions(mockRole, mockTeam, mockUserIdInList); + expect(response).toBe(true); + }); + + it("checkTeamV3Permissions with userid not in team should return true", () => { + let response = teamV3Util.checkTeamV3Permissions(mockRole, mockTeam, mockUserIdInList); + expect(response).toBe(true); + }); + + it("checkTeamV3Permissions for user without manager permisions should return false", () => { + let response = teamV3Util.checkTeamV3Permissions(mockRoleManager, mockTeam, mockUserIdInListNoManager); + expect(response).toBe(false); + }); + + it("checkTeamV3Permissions with empty team should return false", () => { + let response = teamV3Util.checkTeamV3Permissions(mockRole, mockTeamEmpty, mockUserIdNotInList); + expect(response).toBe(false); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/__tests__/formatTeamMembers.test.js b/src/resources/utilities/__tests__/formatTeamMembers.test.js new file mode 100644 index 00000000..8faebfac --- /dev/null +++ b/src/resources/utilities/__tests__/formatTeamMembers.test.js @@ -0,0 +1,14 @@ +import teamV3Util from '../team.v3.util'; +import { mockTeam, mockResponse } from '../__mocks__/formatTeamMembers.mock'; + +describe("formatTeamMembers test", () => { + it("should return empty object for empty input", () => { + let response = teamV3Util.formatTeamMembers({}); + expect(response).toMatchObject({}); + }); + + it("should return response as expected", () => { + let response = teamV3Util.formatTeamMembers(mockTeam); + expect(response).toMatchObject(mockResponse); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js new file mode 100644 index 00000000..a569ea1c --- /dev/null +++ b/src/resources/utilities/team.v3.util.js @@ -0,0 +1,93 @@ +import _, { isEmpty, has } from 'lodash'; +import constants from './constants.util'; + +/** + * Check a users permission levels for a team + * + * @param {enum} role The role required for the action + * @param {object} team The team object containing its members + * @param {objectId} userId The userId to check the permissions for + */ +const checkTeamV3Permissions = (role, team, userId) => { + if (has(team, 'members')) { + let { members } = team; + let userMember = members.find(el => el.memberid.toString() === userId.toString()); + + if (userMember) { + let { roles = [] } = userMember; + if (roles.includes(role) || roles.includes(constants.roleTypes.MANAGER) || role === '') { + return true; + } + } + } + return false; +}; + +/** + * Check if admin + * + * @param {object} user The user object + * @param {array} adminRoles The adminRoles to check + */ +const checkIfAdmin = (user, adminRoles) => { + let { teams } = user; + if (teams) { + teams = teams.map(team => { + let { publisher, type, members } = team; + let member = members.find(member => { + return member.memberid.toString() === user._id.toString(); + }); + let { roles } = member; + return { ...publisher, type, roles }; + }); + } + const isAdmin = teams.filter(team => team.type === constants.teamTypes.ADMIN); + if (!isEmpty(isAdmin)) { + if (isAdmin[0].roles.some(role => adminRoles.includes(role))) { + return true; + } + } + + return false; +}; + +/** + * format output team members + * + * @param {object} team The team object + */ +const formatTeamMembers = team => { + let { users = [] } = team; + users = users.map(user => { + if (user.id) { + let { + firstname, + lastname, + id, + _id, + email, + additionalInfo: { organisation, bio, showOrganisation, showBio }, + } = user; + let userMember = team.members.find(el => el.memberid.toString() === user._id.toString()); + let { roles = [] } = userMember; + return { + firstname, + lastname, + userId: _id.toString(), + email, + roles, + organisation: showOrganisation ? organisation : '', + bio: showBio ? bio : '', + }; + } + }); + return users.filter(user => { + return user; + }); +}; + +export default { + checkTeamV3Permissions, + checkIfAdmin, + formatTeamMembers, +} \ No newline at end of file From ab1b90171cf8a1797c55b569e98e8c0462d0ae6c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 23 Jan 2023 14:33:29 +0000 Subject: [PATCH 060/229] delete member from team --- src/resources/team/team.model.js | 12 +- src/resources/team/v3/team.controller.js | 66 +++++++-- src/resources/team/v3/team.route.js | 8 ++ src/resources/team/v3/team.service.js | 25 +++- .../utilities/__mocks__/getTeamName.mock.js | 136 ++++++++++++++++++ .../utilities/__tests__/getTeamName.test.js | 16 +++ src/resources/utilities/team.v3.util.js | 117 ++++++++++++++- test/{routes.test.js => routes.wrong.js} | 0 8 files changed, 368 insertions(+), 12 deletions(-) create mode 100644 src/resources/utilities/__mocks__/getTeamName.mock.js create mode 100644 src/resources/utilities/__tests__/getTeamName.test.js rename test/{routes.test.js => routes.wrong.js} (100%) diff --git a/src/resources/team/team.model.js b/src/resources/team/team.model.js index 520eaa52..b4bba986 100644 --- a/src/resources/team/team.model.js +++ b/src/resources/team/team.model.js @@ -12,7 +12,17 @@ const TeamSchema = new Schema( { _id: false, memberid: { type: Schema.Types.ObjectId, ref: 'User', required: true }, - roles: { type: [String], enum: ['reviewer', 'manager', 'metadata_editor'], required: true }, + roles: { + type: [String], + enum: [ + 'reviewer', + 'manager', + 'metadata_editor', + 'custodian.team.admin', + 'custodian.metadata.manager', + 'custodian.dar.manager' + ], + required: true }, dateCreated: Date, dateUpdated: Date, notifications: [ diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 1612917d..d3d2b011 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -11,26 +11,74 @@ class TeamController extends TeamService { async getTeamMembers(req, res) { const teamId = req.params.teamid; - const user = req.user; + const users = req.user; const currentUserId = req.user._id; const team = await this.getMembersByTeamId(teamId); - let authorised = teamV3Util.checkTeamV3Permissions('', team.toObject(), currentUserId); - if (!authorised) { - authorised = teamV3Util.checkIfAdmin(user, [constants.roleTypes.ADMIN_DATASET]); - } - if (!authorised) { - return res.status(401).json({ success: false }); - } + this.checkUserAuthorization(currentUserId, '', team, users); let members = teamV3Util.formatTeamMembers(team); -console.log(members); + res.status(200).json({ members, }); } + async deleteTeamMember(req, res) { + const teamId = req.params.teamid; + const deleteUserId = req.params.memberid; + const userObj = req.user; + const currentUserId = req.user._id; + + const team = await this.getTeamByTeamId(teamId); + + this.checkUserAuthorization(currentUserId, 'manager', team, userObj); + + let { members = [], users = [] } = team; + + this.checkIfLastManager(members, deleteUserId); + + let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); + if (members.length === updatedMembers.length) { + throw new Error(`The user requested for deletion is not a member of this team.`); + } + + team.members = updatedMembers; + try { + team.save(function (err, result) { + if (err) { + throw new Error(err.message); + } else { + let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); + return res.status(204).json({ + success: true, + }); + } + }); + } catch (e) { + throw new Error(e.message); + } + } + + checkUserAuthorization(currUserId, permission, team, users) { + let authorised = teamV3Util.checkTeamV3Permissions(permission, team.toObject(), currUserId); + if (!authorised) { + authorised = teamV3Util.checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); + } + if (!authorised) { + throw new Error(`Not enough permissions. User is not authorized to perform this action.`); + } + } + + checkIfLastManager(members, deleteUserId) { + let managerCount = members.filter(mem => mem.roles.includes('manager') && mem.memberid.toString() !== deleteUserId).length; + if (managerCount === 0) { + throw new Error(`You cannot delete the last manager in the team.`); + } + } + } module.exports = new TeamController(); \ No newline at end of file diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index 9eca7c81..bcc13eec 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -10,4 +10,12 @@ const router = express.Router(); // @access Private router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.getTeamMembers(req, res)); +// @route DELETE api/v3/teams/:teamid/members/memberid +// @desc DELETE team member from the team +// @access Private +router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.deleteTeamMember(req, res)); + +// test +// memberid: 628f9e65b089fa694655d168 + module.exports = router; \ No newline at end of file diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js index 5d8db47a..b859166a 100644 --- a/src/resources/team/v3/team.service.js +++ b/src/resources/team/v3/team.service.js @@ -23,4 +23,27 @@ export default class TeamService { throw new Error(e.message); } } -} + + async getTeamByTeamId(teamId) { + try { + const team = await TeamModel + .findOne({ _id: teamId }) + .populate([ + { path: 'users' }, + { + path: 'publisher', + select: 'name' + } + ]); + + if (!team) { + throw new Error(`Team not Found`); + } + + return team; + } catch (e) { + process.stdout.write(`TeamController.getTeamByTeamId : ${e.message}\n`); + throw new Error(e.message); + } + } +} \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/getTeamName.mock.js b/src/resources/utilities/__mocks__/getTeamName.mock.js new file mode 100644 index 00000000..05f0e9a1 --- /dev/null +++ b/src/resources/utilities/__mocks__/getTeamName.mock.js @@ -0,0 +1,136 @@ +const mockTeamWithPublisher = { + "active": true, + "_id": "63bbebf8ec565a91c474cd1b", + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + } + ], + "notifications": [], + "type": "publisher", + "createdAt": "2023-01-09T10:27:04.376Z", + "updatedAt": "2023-01-23T13:59:48.253Z", + "__v": 25, + "publisher": { + "_id": "63bbebf8ec565a91c474cd1b", + "name": "ALLIANCE > Test40" + }, + "users": [ + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "6308bfd1d2ff69e6c13427e7", + "id": 6531262197049297, + "providerId": "102868775144293907483", + "provider": "google", + "firstname": "kymme", + "lastname": "hayley", + "email": "kymme@hdruk.dev", + "role": "Admin", + "createdAt": "2022-08-26T12:42:57.116Z", + "updatedAt": "2023-01-06T16:08:52.920Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "fa596dcd0486d6919c9ee98db5eb00429341d266e275c3c5d8b95b21ff27b89f", + "discourseUsername": "kymme.hayley" + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "5e8c3823e63e5d83ac27c347", + "id": 5890232553870074, + "providerId": "102167422686846649659", + "provider": "google", + "firstname": "Ciara", + "lastname": "Ward", + "email": "ciara.ward@paconsulting.com", + "password": null, + "role": "Creator", + "__v": 0, + "discourseKey": "a23c62c2f9b06f0873a567522ac585a288af9fa8ec7b62eeec68baebef1cdf10", + "discourseUsername": "ciara.ward", + "updatedAt": "2021-05-12T09:51:49.573Z", + "createdAt": "2020-09-04T00:00:00.000Z" + } + ] +}; + +const mockTeamWithoutPublisher = { + "active": true, + "_id": "63bbebf8ec565a91c474cd1b", + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + } + ], + "notifications": [], + "type": "publisher", + "createdAt": "2023-01-09T10:27:04.376Z", + "updatedAt": "2023-01-23T13:59:48.253Z", + "__v": 25, + "users": [ + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "6308bfd1d2ff69e6c13427e7", + "id": 6531262197049297, + "providerId": "102868775144293907483", + "provider": "google", + "firstname": "kymme", + "lastname": "hayley", + "email": "kymme@hdruk.dev", + "role": "Admin", + "createdAt": "2022-08-26T12:42:57.116Z", + "updatedAt": "2023-01-06T16:08:52.920Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "fa596dcd0486d6919c9ee98db5eb00429341d266e275c3c5d8b95b21ff27b89f", + "discourseUsername": "kymme.hayley" + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "5e8c3823e63e5d83ac27c347", + "id": 5890232553870074, + "providerId": "102167422686846649659", + "provider": "google", + "firstname": "Ciara", + "lastname": "Ward", + "email": "ciara.ward@paconsulting.com", + "password": null, + "role": "Creator", + "__v": 0, + "discourseKey": "a23c62c2f9b06f0873a567522ac585a288af9fa8ec7b62eeec68baebef1cdf10", + "discourseUsername": "ciara.ward", + "updatedAt": "2021-05-12T09:51:49.573Z", + "createdAt": "2020-09-04T00:00:00.000Z" + } + ] +}; + +export { + mockTeamWithPublisher, + mockTeamWithoutPublisher, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/getTeamName.test.js b/src/resources/utilities/__tests__/getTeamName.test.js new file mode 100644 index 00000000..e4127fc4 --- /dev/null +++ b/src/resources/utilities/__tests__/getTeamName.test.js @@ -0,0 +1,16 @@ +import teamV3Util from '../team.v3.util'; +import { mockTeamWithPublisher, mockTeamWithoutPublisher } from '../__mocks__/getTeamName.mock'; + +describe('getTeamName test', () => { + it('should return a string who contain the publisher name', () => { + let response = teamV3Util.getTeamName(mockTeamWithPublisher); + expect(typeof response).toBe('string') + expect(response).toContain('ALLIANCE > Test40'); + }); + + it('should return a string who contain a generic publisher name', () => { + let response = teamV3Util.getTeamName(mockTeamWithoutPublisher); + expect(typeof response).toBe('string') + expect(response).toContain('No team name'); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index a569ea1c..07d165ca 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -1,5 +1,8 @@ -import _, { isEmpty, has } from 'lodash'; +import _, { isEmpty, has, isNull } from 'lodash'; import constants from './constants.util'; +// import emailGenerator from '../../utilities/emailGenerator.util'; +import emailGenerator from './emailGenerator.util'; +import notificationBuilder from './notificationBuilder'; /** * Check a users permission levels for a team @@ -58,6 +61,7 @@ const checkIfAdmin = (user, adminRoles) => { */ const formatTeamMembers = team => { let { users = [] } = team; + // console.log(users); users = users.map(user => { if (user.id) { let { @@ -86,8 +90,119 @@ const formatTeamMembers = team => { }); }; +const createTeamNotifications = async (type, context, team, user, publisherId) => { + let teamName; + if (type !== 'TeamAdded') { + teamName = getTeamName(team); + } + let options = {}; + let html = ''; + + switch (type) { + case constants.notificationTypes.MEMBERREMOVED: + // 1. Get user removed + const { removedUser } = context; + // 2. Create user notifications + notificationBuilder.triggerNotificationMessage( + [removedUser.id], + `You have been removed from the team ${teamName}`, + 'team unlinked', + teamName + ); + // 3. Create email + options = { + teamName, + }; + html = emailGenerator.generateRemovedFromTeam(options); + await emailGenerator.sendEmail([removedUser], constants.hdrukEmail, `You have been removed from the team ${teamName}`, html, false); + break; + case constants.notificationTypes.MEMBERADDED: + // 1. Get users added + const { newUsers } = context; + const newUserIds = newUsers.map(user => user.id); + // 2. Create user notifications + notificationBuilder.triggerNotificationMessage( + newUserIds, + `You have been added to the team ${teamName} on the HDR UK Innovation Gateway`, + 'team', + teamName + ); + // 3. Create email for reviewers + options = { + teamName, + role: constants.roleTypes.REVIEWER, + }; + html = emailGenerator.generateAddedToTeam(options); + await emailGenerator.sendEmail( + newUsers, + constants.hdrukEmail, + `You have been added as a reviewer to the team ${teamName} on the HDR UK Innovation Gateway`, + html, + false + ); + // 4. Create email for managers + options = { + teamName, + role: constants.roleTypes.MANAGER, + }; + html = emailGenerator.generateAddedToTeam(options); + await emailGenerator.sendEmail( + newUsers, + constants.hdrukEmail, + `You have been added as a manager to the team ${teamName} on the HDR UK Innovation Gateway`, + html, + false + ); + break; + case constants.notificationTypes.TEAMADDED: + const { recipients } = context; + const recipientIds = recipients.map(recipient => recipient.id); + //1. Create notifications + notificationBuilder.triggerNotificationMessage( + recipientIds, + `You have been assigned as a team manger to the team ${team}`, + 'team added', + team, + publisherId + ); + //2. Create email + options = { + team, + }; + html = emailGenerator.generateNewTeamManagers(options); + await emailGenerator.sendEmail( + recipients, + constants.hdrukEmail, + `You have been assigned as a team manger to the team ${team}`, + html, + false + ); + break; + case constants.notificationTypes.MEMBERROLECHANGED: + break; + } +}; + +/** + * Extract the name of a team from MongoDb object + * + * @param {object} team The team object containing its name or linked object containing name e.g. publisher + */ +const getTeamName = team => { + if (has(team, 'publisher') && !isNull(team.publisher)) { + let { + publisher: { name }, + } = team; + return name; + } else { + return 'No team name'; + } +}; + export default { checkTeamV3Permissions, checkIfAdmin, formatTeamMembers, + createTeamNotifications, + getTeamName, } \ No newline at end of file diff --git a/test/routes.test.js b/test/routes.wrong.js similarity index 100% rename from test/routes.test.js rename to test/routes.wrong.js From d9e4ba105d49b32697000f4c9af3cc666304f970 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 23 Jan 2023 14:36:46 +0000 Subject: [PATCH 061/229] cosmetic update --- src/resources/team/team.model.js | 3 ++- src/resources/utilities/team.v3.util.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/resources/team/team.model.js b/src/resources/team/team.model.js index b4bba986..5549bf57 100644 --- a/src/resources/team/team.model.js +++ b/src/resources/team/team.model.js @@ -22,7 +22,8 @@ const TeamSchema = new Schema( 'custodian.metadata.manager', 'custodian.dar.manager' ], - required: true }, + required: true + }, dateCreated: Date, dateUpdated: Date, notifications: [ diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 07d165ca..40ac53d2 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -203,6 +203,6 @@ export default { checkTeamV3Permissions, checkIfAdmin, formatTeamMembers, - createTeamNotifications, - getTeamName, + createTeamNotifications, + getTeamName, } \ No newline at end of file From 70cb216b4b0b3788c889ddd3925eb1f60b1c65d7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 23 Jan 2023 14:37:34 +0000 Subject: [PATCH 062/229] cosmetic update --- src/resources/team/v3/team.route.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index bcc13eec..da5d31d2 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -15,7 +15,5 @@ router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamC // @access Private router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.deleteTeamMember(req, res)); -// test -// memberid: 628f9e65b089fa694655d168 module.exports = router; \ No newline at end of file From 79e1315b84563e7ebd9473f8c6f4ce2d1005eaa0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 23 Jan 2023 14:38:15 +0000 Subject: [PATCH 063/229] cosmetic update --- src/resources/team/v3/team.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index da5d31d2..65e37004 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -10,7 +10,7 @@ const router = express.Router(); // @access Private router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.getTeamMembers(req, res)); -// @route DELETE api/v3/teams/:teamid/members/memberid +// @route DELETE api/v3/teams/:teamid/members/:memberid // @desc DELETE team member from the team // @access Private router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.deleteTeamMember(req, res)); From 61258063dc3d1668e9a132c53f3eb78de31175c4 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Mon, 23 Jan 2023 16:02:08 +0000 Subject: [PATCH 064/229] GAT-1932: Added java heap parameters --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c1fc8600..18cdcfc3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,4 @@ RUN npm install --production COPY . . EXPOSE 3001 -CMD [ "node", "index.js" ] +CMD [ "node","--max-old-space-size=3072", "index.js" ] From c5a39750305247df72037d23a2dd2e784302af44 Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 24 Jan 2023 13:50:19 +0000 Subject: [PATCH 065/229] GAT-1932 fixes api crash due to excessive data loading --- .../dataUseRegister.controller.js | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index a50fc24e..e1ac6518 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -278,22 +278,25 @@ export default class DataUseRegisterController extends Controller { as: 'publisherDetails', }, }, - { - $lookup: { - from: 'tools', - localField: 'gatewayOutputsTools', - foreignField: 'id', - as: 'gatewayOutputsToolsInfo', - }, - }, - { - $lookup: { - from: 'tools', - localField: 'gatewayOutputsPapers', - foreignField: 'id', - as: 'gatewayOutputsPapersInfo', - }, - }, + // Removed for now, see comments on: + // https://hdruk.atlassian.net/browse/GAT-1932 + // + // { + // $lookup: { + // from: 'tools', + // localField: 'gatewayOutputsTools', + // foreignField: 'id', + // as: 'gatewayOutputsToolsInfo', + // }, + // }, + // { + // $lookup: { + // from: 'tools', + // localField: 'gatewayOutputsPapers', + // foreignField: 'id', + // as: 'gatewayOutputsPapersInfo', + // }, + // }, { $lookup: { from: 'users', @@ -350,9 +353,28 @@ export default class DataUseRegisterController extends Controller { aggregateQuery.unshift({ $match: { $text: { $search: searchString } } }); } - const result = await DataUseRegister.aggregate(aggregateQuery); + const results = await DataUseRegister.aggregate(aggregateQuery); + let newPayload = []; + + // Due to the excessive size of laySummary and publicBenefitStatement + // we have to truncate the content to avoid running out of memory + // + // TODO - Needs refactoring on the whole + results.forEach(result => { + if (result.laySummary) { + result.laySummary = result.laySummary.substring(0, 200); + result.laySummary += '...'; + } - return res.status(200).json({ success: true, result }); + if (result.publicBenefitStatement) { + result.publicBenefitStatement = result.publicBenefitStatement.substring(0, 200) + result.publicBenefitStatement += '...'; + } + + newPayload.push(result); + }); + + return res.status(200).json({ success: true, newPayload }); } catch (err) { //Return error response if something goes wrong logger.logError(err, logCategory); From b89fb0f4292f7e1d7466f9579d04b028be4036ee Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:52:41 +0000 Subject: [PATCH 066/229] GAT-1932: Remove the max_old_space parameter --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 18cdcfc3..c1fc8600 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,4 @@ RUN npm install --production COPY . . EXPOSE 3001 -CMD [ "node","--max-old-space-size=3072", "index.js" ] +CMD [ "node", "index.js" ] From 73d1d6d63e5bf03cec6112a841c7d16b222e4eae Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:26:17 +0000 Subject: [PATCH 067/229] GAT-1932: Adding Fixes --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1bfaf023..33eddae9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ "dev", UAT, UATBeta, master, release ] + branches: [ "dev", "UAT", "UATBeta", "master", "release" ] pull_request: # The branches below must be a subset of the branches above - branches: [ "dev" ] + branches: [ "dev", "UAT", "UATBeta", "master", "release" ] schedule: - cron: '26 5 * * 1' From e3b080fcf348adb8631f836c975abdd463eb59b7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 25 Jan 2023 10:03:42 +0000 Subject: [PATCH 068/229] create new member in a team --- src/exceptions/HttpExceptions.js | 4 +- src/middlewares/errorHandler.middleware.js | 12 +- src/resources/team/v3/team.controller.js | 65 +- src/resources/team/v3/team.route.js | 6 + src/resources/team/v3/team.service.js | 2 +- .../__mocks__/checkUserAuthorization.mock.js | 1365 +++++++++++++++++ .../__tests__/checkUserAuthorization.test.js | 14 + src/resources/utilities/team.v3.util.js | 22 +- 8 files changed, 1464 insertions(+), 26 deletions(-) create mode 100644 src/resources/utilities/__mocks__/checkUserAuthorization.mock.js create mode 100644 src/resources/utilities/__tests__/checkUserAuthorization.test.js diff --git a/src/exceptions/HttpExceptions.js b/src/exceptions/HttpExceptions.js index be980e7a..77b251b9 100644 --- a/src/exceptions/HttpExceptions.js +++ b/src/exceptions/HttpExceptions.js @@ -1,6 +1,6 @@ export default class HttpExceptions extends Error { - constructor(message) { + constructor(message, statusCode = 500) { super(message); - this.message = message; + this.status = statusCode; } } \ No newline at end of file diff --git a/src/middlewares/errorHandler.middleware.js b/src/middlewares/errorHandler.middleware.js index 9c54aabe..03c99208 100644 --- a/src/middlewares/errorHandler.middleware.js +++ b/src/middlewares/errorHandler.middleware.js @@ -6,17 +6,23 @@ const errorHandler = (error, req, res, next) => { const loggingService = new LoggingService(); const loggingEnabled = parseInt(process.env.LOGGING_LOG_ENABLED) || 0; - const errorMessage = { + const errorResponseMessage = { + type: 'error', + message: error.message, + }; + const errorFullMessage = { type: 'error', message: error.message, stack: error.stack.split("\n"), }; + process.stdout.write(JSON.stringify(errorFullMessage)); + if (loggingEnabled) { - loggingService.sendDataInLogging(errorMessage, 'ERROR'); + loggingService.sendDataInLogging(errorFullMessage, 'ERROR'); } - res.status(errorStatusCode).json(errorMessage); + res.status(errorStatusCode).json(errorResponseMessage); return; } diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index d3d2b011..d75c9308 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -3,6 +3,9 @@ import TeamService from './team.service'; import teamV3Util from '../../utilities/team.v3.util'; import constants from '../../utilities/constants.util'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; +import { UserModel } from '../../user/user.model'; +import { TeamModel } from '../team.model'; class TeamController extends TeamService { constructor() { @@ -16,7 +19,7 @@ class TeamController extends TeamService { const team = await this.getMembersByTeamId(teamId); - this.checkUserAuthorization(currentUserId, '', team, users); + teamV3Util.checkUserAuthorization(currentUserId, '', team, users); let members = teamV3Util.formatTeamMembers(team); @@ -33,11 +36,11 @@ class TeamController extends TeamService { const team = await this.getTeamByTeamId(teamId); - this.checkUserAuthorization(currentUserId, 'manager', team, userObj); + teamV3Util.checkUserAuthorization(currentUserId, 'manager', team, userObj); let { members = [], users = [] } = team; - this.checkIfLastManager(members, deleteUserId); + teamV3Util.checkIfLastManager(members, deleteUserId); let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); if (members.length === updatedMembers.length) { @@ -62,23 +65,47 @@ class TeamController extends TeamService { } } - checkUserAuthorization(currUserId, permission, team, users) { - let authorised = teamV3Util.checkTeamV3Permissions(permission, team.toObject(), currUserId); - if (!authorised) { - authorised = teamV3Util.checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); - } - if (!authorised) { - throw new Error(`Not enough permissions. User is not authorized to perform this action.`); - } - } + async addTeamMember(req, res) { + const teamId = req.params.teamid; + const userObj = req.user; + const currentUserId = req.user._id; + const { memberid, roles = [] } = req.body; - checkIfLastManager(members, deleteUserId) { - let managerCount = members.filter(mem => mem.roles.includes('manager') && mem.memberid.toString() !== deleteUserId).length; - if (managerCount === 0) { - throw new Error(`You cannot delete the last manager in the team.`); - } - } + const team = await this.getTeamByTeamId(teamId); + teamV3Util.checkUserAuthorization(currentUserId, 'manager', team, userObj); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === memberid.toString()); + if (checkIfExistMember) { + throw new HttpExceptions(`Member already exists`, 409); + } + + let newMembers = { + roles: roles, + memberid: memberid, + notifications: [] + }; + + team.members = team.members.concat(newMembers); + team.save(async err => { + if (err) { + throw new Error(err.message); + } else { + let newUsers = await UserModel.find({ _id: memberid }); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); + // 11. Get updated team users including bio data + const updatedTeam = await this.getMembersByTeamIdmaybe(teamId); + let users = teamV3Util.formatTeamMembers(updatedTeam); + // 12. Return successful response payload + return res.status(201).json({ + success: true, + members: users, + }); + } + }); + } } -module.exports = new TeamController(); \ No newline at end of file +module.exports = new TeamController(); diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index 65e37004..ebf226df 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -16,4 +16,10 @@ router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamC router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.deleteTeamMember(req, res)); +// @route POST api/v3/teams/:teamid/members +// @desc POST add new team member +// @access Private +router.post('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.addTeamMember(req, res)); + + module.exports = router; \ No newline at end of file diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js index b859166a..c7207090 100644 --- a/src/resources/team/v3/team.service.js +++ b/src/resources/team/v3/team.service.js @@ -46,4 +46,4 @@ export default class TeamService { throw new Error(e.message); } } -} \ No newline at end of file +} diff --git a/src/resources/utilities/__mocks__/checkUserAuthorization.mock.js b/src/resources/utilities/__mocks__/checkUserAuthorization.mock.js new file mode 100644 index 00000000..c895143e --- /dev/null +++ b/src/resources/utilities/__mocks__/checkUserAuthorization.mock.js @@ -0,0 +1,1365 @@ +const currUserId = '61f91d232e175937b960e213'; +const permission = 'manager'; + +const mockTeam = { + "active": true, + "_id": "63bbebf8ec565a91c474cd1b", + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "5e8c3823e63e5d83ac27c347", + "notifications": [] + } + ], + "notifications": [], + "type": "publisher", + "createdAt": "2023-01-09T10:27:04.376Z", + "updatedAt": "2023-01-23T13:59:48.253Z", + "__v": 25, + "users": [ + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "6308bfd1d2ff69e6c13427e7", + "id": 6531262197049297, + "providerId": "102868775144293907483", + "provider": "google", + "firstname": "kymme", + "lastname": "hayley", + "email": "kymme@hdruk.dev", + "role": "Admin", + "createdAt": "2022-08-26T12:42:57.116Z", + "updatedAt": "2023-01-06T16:08:52.920Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "fa596dcd0486d6919c9ee98db5eb00429341d266e275c3c5d8b95b21ff27b89f", + "discourseUsername": "kymme.hayley", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": true, + "id": 6531262197049297, + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "organisation": "HDR UK", + "showBio": true + } + }, + { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "5e8c3823e63e5d83ac27c347", + "id": 5890232553870074, + "providerId": "102167422686846649659", + "provider": "google", + "firstname": "Ciara", + "lastname": "Ward", + "email": "ciara.ward@paconsulting.com", + "password": null, + "role": "Creator", + "__v": 0, + "discourseKey": "a23c62c2f9b06f0873a567522ac585a288af9fa8ec7b62eeec68baebef1cdf10", + "discourseUsername": "ciara.ward", + "updatedAt": "2021-05-12T09:51:49.573Z", + "createdAt": "2020-09-04T00:00:00.000Z", + "additionalInfo": { + "emailNotifications": true, + "showOrganisation": false, + "id": 5890232553870074, + "activeflag": "active", + "bio": "", + "link": "", + "orcid": "https://orcid.org/undefined", + "organisation": "", + "terms": true, + "showBio": true + } + } + ] +}; +const mockUsers = { + "feedback": false, + "news": false, + "isServiceAccount": false, + "advancedSearchRoles": [], + "_id": "61f91d232e175937b960e213", + "id": 42412943236984630, + "providerId": "100742128864395249791", + "provider": "google", + "firstname": "Dan", + "lastname": "Nita", + "email": "dan.nita.hdruk@gmail.com", + "role": "Admin", + "createdAt": "2022-02-01T11:44:35.584Z", + "updatedAt": "2022-11-29T10:26:06.318Z", + "__v": 0, + "redirectURL": "/search?search=&tab=Datasets", + "discourseKey": "12e3760a22942a100ea08036b93837a01cf615988fc7d312e50d733d9367d861", + "discourseUsername": "dan.nita", + "teams": [ + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "62837ba8a35a2205b0f0a0b6", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "628cc003cc879f3d43852b8f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f8992a97150a1b050be0712", + "name": "ALLIANCE > PUBLIC HEALTH SCOTLAND" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623467f6ce42aab1cfc29021", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f3f98068af2ef61552e1d75", + "name": "ALLIANCE > SAIL" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61128e7ef7ff9cee652532b4", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "623c81f5aa033e0e643d6c6a", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [ + { + "optIn": true, + "_id": "62c40c6ab1079d7d4ee1c608", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61b9c46dfbd7e9f3aa270ac1" + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6193e832536f42aa8f976fdc", + "notifications": [ + { + "optIn": true, + "_id": "623c515eaa033e6d0d3d6473", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "62839a8883f55d40d3d20cf4", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62ebe58bd7595507ba81d689", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62ed1f7e7d1af36e6d280b8e", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ef9c4ebb9796854174cbf94", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61dd491e4d274e7eac3b848e", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f7b1a2bce9f65e6ed83e7da", + "name": "OTHER > HEALTH DATA RESEARCH UK" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5f8563c6fa9a256698a9fafb", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61854c1e46f52ed51754bb24" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "60191005c13ca825e4c6cadd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f89662f7150a1b050be0710", + "name": "ALLIANCE > HEALTH AND SOCIAL CARE NORTHERN IRELAND" + } + }, + { + "members": [ + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e725692c9a581a0dd2bd84b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e726049c9a58131cd2bd874", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6167edbd5306ac30d5b1da14", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619261978f832546e0b0edfd", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619501876b8724ad01077af6", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61978834a87688d4e67770fa", + "notifications": [] + }, + { + "roles": [ + "admin_data_use" + ], + "memberid": "619ba1e87d2fd8db23885ce9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61b9c46dfbd7e9f3aa270ac1", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61854c1e46f52ed51754bb24", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62349a67767db5d3408b4001", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61e93915826d995980cca3dc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "626a72ba4524adcf224b769b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + } + ], + "type": "admin", + "publisher": null + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62f2335e062fcd05b9e8d2cf", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62f241e8f15b29ce1c24fd96", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62c80ec7970e8a07797c23c7", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "6318aab2f051c6375ec53b7e", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "5fc8e2b7c386587231140f85", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5f7c2bcd504d5e7cda30b3ea", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5e851759b9bbd5ecd9f65a39", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "634c518998e119341680d558", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "61e57fd3012bda94e0e8b9c6", + "name": "OTHER > Priti Test Team" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [ + { + "optIn": true, + "_id": "62389e82e5c72465f718013f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61cc3966928507fe0f1b9325", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "6255a87db588c01f5250a3ce", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "62024195f0e15612a4e16979", + "name": "OTHER > Test1" + } + } + ] +}; + +export { + currUserId, + permission, + mockTeam, + mockUsers, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkUserAuthorization.test.js b/src/resources/utilities/__tests__/checkUserAuthorization.test.js new file mode 100644 index 00000000..32d5de5d --- /dev/null +++ b/src/resources/utilities/__tests__/checkUserAuthorization.test.js @@ -0,0 +1,14 @@ +import teamV3Util from '../team.v3.util'; +import { + currUserId, + permission, + mockTeam, + mockUsers, +} from '../__mocks__/checkUserAuthorization.mock'; + +describe("test checkUserAuthorization", () => { + it("should return true", () => { + let response = teamV3Util.checkUserAuthorization(currUserId, permission, mockTeam, mockUsers); + expect(response).toBe(true); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 40ac53d2..a9738900 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -61,7 +61,6 @@ const checkIfAdmin = (user, adminRoles) => { */ const formatTeamMembers = team => { let { users = [] } = team; - // console.log(users); users = users.map(user => { if (user.id) { let { @@ -199,10 +198,31 @@ const getTeamName = team => { } }; +const checkUserAuthorization = (currUserId, permission, team, users) => { + let authorised = checkTeamV3Permissions(permission, team, currUserId); + if (!authorised) { + authorised = checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); + } + if (!authorised) { + throw new Error(`Not enough permissions. User is not authorized to perform this action.`); + } + + return true; +}; + +const checkIfLastManager = (members, deleteUserId) => { + let managerCount = members.filter(mem => mem.roles.includes('manager') && mem.memberid.toString() !== deleteUserId).length; + if (managerCount === 0) { + throw new Error(`You cannot delete the last manager in the team.`); + } +} + export default { checkTeamV3Permissions, checkIfAdmin, formatTeamMembers, createTeamNotifications, getTeamName, + checkUserAuthorization, + checkIfLastManager, } \ No newline at end of file From 4fdeec7af1fd476e51cd3168b4ac9755023ec3f9 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 25 Jan 2023 10:10:13 +0000 Subject: [PATCH 069/229] update --- src/resources/team/v3/team.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index d75c9308..1dc07563 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -96,7 +96,7 @@ class TeamController extends TeamService { let newUsers = await UserModel.find({ _id: memberid }); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); // 11. Get updated team users including bio data - const updatedTeam = await this.getMembersByTeamIdmaybe(teamId); + const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); // 12. Return successful response payload return res.status(201).json({ From 5aee51a5eda35c79b94b44ebdc4fff62ee897151 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 25 Jan 2023 11:13:56 +0000 Subject: [PATCH 070/229] update --- src/resources/team/v3/team.controller.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 1dc07563..39473bbe 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -69,7 +69,7 @@ class TeamController extends TeamService { const teamId = req.params.teamid; const userObj = req.user; const currentUserId = req.user._id; - const { memberid, roles = [] } = req.body; + const { memberId, roles = [] } = req.body; const team = await this.getTeamByTeamId(teamId); @@ -84,7 +84,7 @@ class TeamController extends TeamService { let newMembers = { roles: roles, - memberid: memberid, + memberid: memberId, notifications: [] }; @@ -93,12 +93,10 @@ class TeamController extends TeamService { if (err) { throw new Error(err.message); } else { - let newUsers = await UserModel.find({ _id: memberid }); + let newUsers = await UserModel.find({ _id: memberId }); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); - // 11. Get updated team users including bio data const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); - // 12. Return successful response payload return res.status(201).json({ success: true, members: users, From abff4242a293c63ddb35d34eb53edb9432e1136b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 25 Jan 2023 11:15:58 +0000 Subject: [PATCH 071/229] update --- src/resources/team/v3/team.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 39473bbe..ca10498b 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -77,7 +77,7 @@ class TeamController extends TeamService { let { members } = team; - let checkIfExistMember = members.find(item => item.memberid.toString() === memberid.toString()); + let checkIfExistMember = members.find(item => item.memberid.toString() === memberId.toString()); if (checkIfExistMember) { throw new HttpExceptions(`Member already exists`, 409); } From dcca6c229dacf022d587b4968dff26bb6d3190a6 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 27 Jan 2023 15:27:05 +0000 Subject: [PATCH 072/229] GAT-1887: Development pipeline for api --- .github/workflows/dev_deployment.yaml | 136 ++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 .github/workflows/dev_deployment.yaml diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml new file mode 100644 index 00000000..40e2ba26 --- /dev/null +++ b/.github/workflows/dev_deployment.yaml @@ -0,0 +1,136 @@ +name: Build and Deploy to Cloud Run + +on: + push: + paths: + - 'Chart.yaml' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: + build: + # Add 'id-token' with the intended permissions for workload identity federation + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-dev + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: GAT-1887 + + - name: Read VERSION file + id: getversion + run: echo "::set-output name=version::$(cat Chart.yaml)" + - uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: ${{ steps.getversion.outputs.version }} + prerelease: false + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Login to GAR + uses: docker/login-action@v2 + with: + registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.PROJECT_ID }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + - name: Build and Push Container + shell: bash + env: + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + PROJECT_ID: ${{ secrets.PROJECT_ID }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + + run: |- + docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} ./ + docker push '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} + # END - Docker auth and build + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + needs: build + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-dev + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: GAT-1887 + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "::set-output name=version::$(cat Chart.yaml)" + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps \ No newline at end of file From 3c07a9318bd20988c91e522dd6a7423b729e623d Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 27 Jan 2023 15:27:52 +0000 Subject: [PATCH 073/229] GAT-1887: Testing --- Chart.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 Chart.yaml diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 00000000..5eb312c6 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1 @@ +v0.0.0 \ No newline at end of file From e0914bbba0725243ed82bde5ffdc9c06f39ef3f3 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Mon, 30 Jan 2023 10:08:27 +0000 Subject: [PATCH 074/229] Tiny change --- src/resources/auth/auth.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/auth/auth.route.js b/src/resources/auth/auth.route.js index fdc342f3..687959c4 100644 --- a/src/resources/auth/auth.route.js +++ b/src/resources/auth/auth.route.js @@ -25,7 +25,7 @@ router.get('/status', function (req, res, next) { if (err || !user) { return res.json({ success: true, - data: [{ role: 'Reader', id: null, name: null, loggedIn: false }], + data: [{ role: 'Reader', id: null, name: null, loggedIn: false, tempProp: true }], }); } else { // 1. Reformat teams array for frontend From 7ff772c6404fc85fe96bc49d1a8b5df68b08baff Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 30 Jan 2023 11:31:18 +0000 Subject: [PATCH 075/229] update roles for member of team --- src/middlewares/errorHandler.middleware.js | 12 +- src/resources/team/v3/team.controller.js | 50 +- src/resources/team/v3/team.route.js | 4 + .../__mocks__/checkAllowNewRoles.mock.js | 26 + .../getAllRolesForApproverUser.mock.js | 1254 +++++++++++++++++ .../__mocks__/listOfRolesAllowed.mock.js | 46 + .../__tests__/checkAllowNewRoles.test.js | 19 + .../getAllRolesForApproverUser.test.js | 10 + .../__tests__/listOfRolesAllowed.test.js | 10 + src/resources/utilities/constants.util.js | 46 + src/resources/utilities/team.v3.util.js | 50 + 11 files changed, 1516 insertions(+), 11 deletions(-) create mode 100644 src/resources/utilities/__mocks__/checkAllowNewRoles.mock.js create mode 100644 src/resources/utilities/__mocks__/getAllRolesForApproverUser.mock.js create mode 100644 src/resources/utilities/__mocks__/listOfRolesAllowed.mock.js create mode 100644 src/resources/utilities/__tests__/checkAllowNewRoles.test.js create mode 100644 src/resources/utilities/__tests__/getAllRolesForApproverUser.test.js create mode 100644 src/resources/utilities/__tests__/listOfRolesAllowed.test.js diff --git a/src/middlewares/errorHandler.middleware.js b/src/middlewares/errorHandler.middleware.js index 03c99208..112bbe06 100644 --- a/src/middlewares/errorHandler.middleware.js +++ b/src/middlewares/errorHandler.middleware.js @@ -6,23 +6,19 @@ const errorHandler = (error, req, res, next) => { const loggingService = new LoggingService(); const loggingEnabled = parseInt(process.env.LOGGING_LOG_ENABLED) || 0; - const errorResponseMessage = { - type: 'error', - message: error.message, - }; - const errorFullMessage = { + const errorMessage = { type: 'error', message: error.message, stack: error.stack.split("\n"), }; - process.stdout.write(JSON.stringify(errorFullMessage)); + process.stdout.write(JSON.stringify(errorMessage)); if (loggingEnabled) { - loggingService.sendDataInLogging(errorFullMessage, 'ERROR'); + loggingService.sendDataInLogging(errorMessage, 'ERROR'); } - res.status(errorStatusCode).json(errorResponseMessage); + res.status(errorStatusCode).json(errorMessage); return; } diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index ca10498b..1a2f7e7d 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -5,7 +5,6 @@ import teamV3Util from '../../utilities/team.v3.util'; import constants from '../../utilities/constants.util'; import HttpExceptions from '../../../exceptions/HttpExceptions'; import { UserModel } from '../../user/user.model'; -import { TeamModel } from '../team.model'; class TeamController extends TeamService { constructor() { @@ -51,7 +50,7 @@ class TeamController extends TeamService { try { team.save(function (err, result) { if (err) { - throw new Error(err.message); + throw new HttpExceptions(err.message); } else { let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); @@ -91,7 +90,7 @@ class TeamController extends TeamService { team.members = team.members.concat(newMembers); team.save(async err => { if (err) { - throw new Error(err.message); + throw new HttpExceptions(err.message); } else { let newUsers = await UserModel.find({ _id: memberId }); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); @@ -104,6 +103,51 @@ class TeamController extends TeamService { } }); } + + async updateTeamMember(req, res) { + const teamId = req.params.teamid; + const updateUserId = req.params.memberid; + const userObj = req.user; + const userTeams = userObj.teams || []; + const currentUserId = req.user._id; + const { roles = [] } = req.body; + + const team = await this.getTeamByTeamId(teamId); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === updateUserId.toString()); + if (!checkIfExistMember) { + throw new HttpExceptions(`The member does not exist in the team`, 409); + } + + const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); + teamV3Util.checkAllowNewRoles(roles, approvedRoles); + + team.members.map(member => { + if (member.memberid.toString() === updateUserId.toString()) { + member.roles = roles; + } + }); + + try { + team.save(async err => { + if (err) { + throw new HttpExceptions(err.message); + } else { + let updatedTeam = await this.getMembersByTeamId(teamId); + let users = teamV3Util.formatTeamMembers(updatedTeam); + return res.json({ + success: true, + members: users, + }); + } + }); + } catch (e) { + throw new HttpExceptions(e.message); + } + } } module.exports = new TeamController(); diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index ebf226df..3968dfac 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -21,5 +21,9 @@ router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, // @access Private router.post('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.addTeamMember(req, res)); +// @route PATCH api/v3/teams/:teamid/members/:memberid +// @desc PATCH add new team member +// @access Private +router.patch('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.updateTeamMember(req, res)); module.exports = router; \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/checkAllowNewRoles.mock.js b/src/resources/utilities/__mocks__/checkAllowNewRoles.mock.js new file mode 100644 index 00000000..209b18ef --- /dev/null +++ b/src/resources/utilities/__mocks__/checkAllowNewRoles.mock.js @@ -0,0 +1,26 @@ +const mockUserUpdateRoles = [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" +]; +const mockUserUpdateRolesFalse = [ + "reviewer", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" +]; +const mockAllowedRoles = [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.metadata.editor", + "custodian.developer", + "custodian.dar.manager", + "custodian.dar.reviewer", + "custodian.dur.manager" +]; + +export { + mockUserUpdateRoles, + mockUserUpdateRolesFalse, + mockAllowedRoles, +} \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/getAllRolesForApproverUser.mock.js b/src/resources/utilities/__mocks__/getAllRolesForApproverUser.mock.js new file mode 100644 index 00000000..c25035a3 --- /dev/null +++ b/src/resources/utilities/__mocks__/getAllRolesForApproverUser.mock.js @@ -0,0 +1,1254 @@ +const mockTeam = [ + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623467f6ce42aab1cfc29021", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f3f98068af2ef61552e1d75", + "name": "ALLIANCE > SAIL" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5f8563c6fa9a256698a9fafb", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61854c1e46f52ed51754bb24" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "60191005c13ca825e4c6cadd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f89662f7150a1b050be0710", + "name": "ALLIANCE > HEALTH AND SOCIAL CARE NORTHERN IRELAND" + } + }, + { + "members": [ + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e725692c9a581a0dd2bd84b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "5e726049c9a58131cd2bd874", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "6167edbd5306ac30d5b1da14", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619261978f832546e0b0edfd", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "619501876b8724ad01077af6", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61978834a87688d4e67770fa", + "notifications": [] + }, + { + "roles": [ + "admin_data_use" + ], + "memberid": "619ba1e87d2fd8db23885ce9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61b9c46dfbd7e9f3aa270ac1", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "61854c1e46f52ed51754bb24", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62349a67767db5d3408b4001", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "admin_dataset" + ], + "memberid": "61e93915826d995980cca3dc", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "626a72ba4524adcf224b769b", + "notifications": [] + }, + { + "roles": [ + "admin_dataset", + "admin_data_use", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + } + ], + "type": "admin", + "publisher": null + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [ + { + "optIn": true, + "_id": "62389e82e5c72465f718013f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "613a0399e6b8113f0a5e0ccc", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "6139c0d1e6b81112ad5e0312", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61cc3966928507fe0f1b9325", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "6255a87db588c01f5250a3ce", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "62024195f0e15612a4e16979", + "name": "OTHER > Test1" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "62837ba8a35a2205b0f0a0b6", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "628cc003cc879f3d43852b8f", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f8992a97150a1b050be0712", + "name": "ALLIANCE > PUBLIC HEALTH SCOTLAND" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61128e7ef7ff9cee652532b4", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "623c81f5aa033e0e643d6c6a", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [ + { + "optIn": true, + "_id": "62c40c6ab1079d7d4ee1c608", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61b9c46dfbd7e9f3aa270ac1" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [ + { + "optIn": true, + "_id": "62839a8883f55d40d3d20cf4", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62ebe58bd7595507ba81d689", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62ed1f7e7d1af36e6d280b8e", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ef9c4ebb9796854174cbf94", + "notifications": [] + }, + { + "roles": [ + "custodian.metadata.manager" + ], + "memberid": "61dd491e4d274e7eac3b848e", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin" + ], + "memberid": "63d2bfcbd4663faea8745ae6", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "custodian.dar.manager" + ], + "memberid": "63d27a30a5959c9bfc72caa2", + "notifications": [] + }, + { + "roles": [ + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "63d3cb845487686dad9552ea", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "5f7b1a2bce9f65e6ed83e7da", + "name": "OTHER > HEALTH DATA RESEARCH UK" + } + }, + { + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "611a58bb9f17737532ad25c0", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "618008d49566b41988d1be02", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62e79d3a892f2fbc28bc233b", + "notifications": [ + { + "optIn": true, + "_id": "62f2335e062fcd05b9e8d2cf", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d5e29ad375f198868e4dc7", + "notifications": [ + { + "optIn": true, + "_id": "62f241e8f15b29ce1c24fd96", + "notificationType": "dataAccessRequest" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "62c80ec7970e8a07797c23c7", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "6318aab2f051c6375ec53b7e", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbd0628f427fb9473287b", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "5fc8e2b7c386587231140f85", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5f7c2bcd504d5e7cda30b3ea", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "5e851759b9bbd5ecd9f65a39", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "634c518998e119341680d55a", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61a5fda2b2d8db3617d85b4b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + } + ], + "type": "publisher", + "publisher": { + "_id": "61e57fd3012bda94e0e8b9c6", + "name": "OTHER > Priti Test Team" + } + } +]; +const mockTeamId = "63bbebf8ec565a91c474cd1b"; +const mockUserId = "61f91d232e175937b960e213"; +const mockResponse = ["admin_dataset","admin_data_use","manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"] + +export { + mockTeam, + mockTeamId, + mockUserId, + mockResponse, +}; \ No newline at end of file diff --git a/src/resources/utilities/__mocks__/listOfRolesAllowed.mock.js b/src/resources/utilities/__mocks__/listOfRolesAllowed.mock.js new file mode 100644 index 00000000..4b9c8998 --- /dev/null +++ b/src/resources/utilities/__mocks__/listOfRolesAllowed.mock.js @@ -0,0 +1,46 @@ +const mockUserRoles = [ + "admin_dataset", + "admin_data_use", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" +]; +const mockRolesAcceptedByRoles = { + "custodian.team.admin": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.metadata.editor", + "custodian.developer", + "custodian.dar.manager", + "custodian.dar.reviewer", + "custodian.dur.manager" + ], + "custodian.metadata.manager": [ + "custodian.metadata.manager", + "custodian.metadata.editor" + ], + "custodian.metadata.editor": [ + "custodian.metadata.editor" + ], + "custodian.developer": [ + "custodian.developer" + ], + "custodian.dar.manager": [ + "custodian.dar.manager", + "custodian.dar.reviewer" + ], + "custodian.dar.reviewer": [ + "custodian.dar.reviewer" + ], + "custodian.dur.manager": [ + "custodian.dur.manager" + ] +}; +const mockResponse = ["custodian.team.admin","custodian.metadata.manager","custodian.metadata.editor","custodian.developer","custodian.dar.manager","custodian.dar.reviewer","custodian.dur.manager"]; + +export { + mockUserRoles, + mockRolesAcceptedByRoles, + mockResponse, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkAllowNewRoles.test.js b/src/resources/utilities/__tests__/checkAllowNewRoles.test.js new file mode 100644 index 00000000..528d135d --- /dev/null +++ b/src/resources/utilities/__tests__/checkAllowNewRoles.test.js @@ -0,0 +1,19 @@ +import teamV3Util from '../team.v3.util'; +import { mockUserUpdateRoles, mockUserUpdateRolesFalse, mockAllowedRoles } from '../__mocks__/checkAllowNewRoles.mock'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; + +describe('checkAllowNewRoles test', () => { + it('should return true', () => { + let response = teamV3Util.checkAllowNewRoles(mockUserUpdateRoles, mockAllowedRoles); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return an exception', () => { + try { + teamV3Util.checkAllowNewRoles(mockUserUpdateRolesFalse, mockAllowedRoles); + } catch (error) { + expect(error).toBeInstanceOf(HttpExceptions); + } + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/__tests__/getAllRolesForApproverUser.test.js b/src/resources/utilities/__tests__/getAllRolesForApproverUser.test.js new file mode 100644 index 00000000..bc274b4a --- /dev/null +++ b/src/resources/utilities/__tests__/getAllRolesForApproverUser.test.js @@ -0,0 +1,10 @@ +import teamV3Util from '../team.v3.util'; +import { mockTeam, mockTeamId, mockUserId, mockResponse } from '../__mocks__/getAllRolesForApproverUser.mock'; + +describe('getAllRolesForApproverUser test', () => { + it('should return array', () => { + let response = teamV3Util.getAllRolesForApproverUser(mockTeam, mockTeamId, mockUserId); + expect(typeof response).toBe('object') + expect(response).toEqual(expect.arrayContaining(mockResponse)); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/__tests__/listOfRolesAllowed.test.js b/src/resources/utilities/__tests__/listOfRolesAllowed.test.js new file mode 100644 index 00000000..e00a8644 --- /dev/null +++ b/src/resources/utilities/__tests__/listOfRolesAllowed.test.js @@ -0,0 +1,10 @@ +import teamV3Util from '../team.v3.util'; +import { mockUserRoles, mockRolesAcceptedByRoles, mockResponse } from '../__mocks__/listOfRolesAllowed.mock'; + +describe('listOfRolesAllowed test', () => { + it('should return array', () => { + let response = teamV3Util.listOfRolesAllowed(mockUserRoles, mockRolesAcceptedByRoles); + expect(typeof response).toBe('object') + expect(response).toEqual(expect.arrayContaining(mockResponse)); + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index 956ac338..b2f038c6 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -345,6 +345,50 @@ const _searchDataTypes = { Datauses: 'dataUseRegister', }; +// update - iam 2 + +const _roleMemberTeam = { + CUST_TEAM_ADMIN: 'custodian.team.admin', + CUST_MD_MANAGER: 'custodian.metadata.manager', + CUST_MD_EDITOR: 'custodian.metadata.editor', + CUST_DEVELOPER: 'custodian.developer', + CUST_DAR_MANAGER: 'custodian.dar.manager', + CUST_DAR_REVIEWER: 'custodian.dar.reviewer', + CUST_DUR_MANAGER: 'custodian.dur.manager', +}; + +const _rolesAcceptedByRoles = { + 'custodian.team.admin': [ + 'custodian.team.admin', + 'custodian.metadata.manager', + 'custodian.metadata.editor', + 'custodian.developer', + 'custodian.dar.manager', + 'custodian.dar.reviewer', + 'custodian.dur.manager', + ], + 'custodian.metadata.manager': [ + 'custodian.metadata.manager', + 'custodian.metadata.editor', + ], + 'custodian.metadata.editor': [ + 'custodian.metadata.editor', + ], + 'custodian.developer': [ + 'custodian.developer', + ], + 'custodian.dar.manager': [ + 'custodian.dar.manager', + 'custodian.dar.reviewer', + ], + 'custodian.dar.reviewer': [ + 'custodian.dar.reviewer', + ], + 'custodian.dur.manager': [ + 'custodian.dur.manager', + ], +}; + export default { userTypes: _userTypes, enquiryFormId: _enquiryFormId, @@ -380,4 +424,6 @@ export default { dataUseRegisterStatus: _dataUseRegisterStatus, dataUseRegisterNotifications: _dataUseRegisterNotifications, searchDataTypes: _searchDataTypes, + roleMemberTeam: _roleMemberTeam, + rolesAcceptedByRoles: _rolesAcceptedByRoles, }; diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index a9738900..4b1aaed3 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -3,6 +3,7 @@ import constants from './constants.util'; // import emailGenerator from '../../utilities/emailGenerator.util'; import emailGenerator from './emailGenerator.util'; import notificationBuilder from './notificationBuilder'; +import HttpExceptions from '../../exceptions/HttpExceptions'; /** * Check a users permission levels for a team @@ -217,6 +218,52 @@ const checkIfLastManager = (members, deleteUserId) => { } } +const getAllRolesForApproverUser = (team, teamId, userId) => { + let arrayRoles = []; + + team.map(publisher => { + if (publisher && publisher.type === constants.teamTypes.ADMIN) { + publisher.members.map(member => { + if (member.memberid.toString() === userId.toString()) { + arrayRoles = [...arrayRoles, ...member.roles]; + } + }); + } + + if (publisher && publisher.type === 'publisher' && publisher.publisher._id.toString() === teamId.toString()) { + publisher.members.map(member => { + if (member.memberid.toString() === userId.toString()) { + arrayRoles = [...arrayRoles, ...member.roles]; + } + }); + } + }); + + return [...new Set(arrayRoles)]; +} + +const listOfRolesAllowed = (userRoles, rolesAcceptedByRoles) => { + let allowedRoles = []; + + userRoles.map(uRole => { + if (rolesAcceptedByRoles[uRole]) { + rolesAcceptedByRoles[uRole].forEach(element => allowedRoles.push(element)); + } + }); + + return [... new Set(allowedRoles)]; +} + +const checkAllowNewRoles = (userUpdateRoles, allowedRoles) => { + userUpdateRoles.forEach(uRole => { + if (!allowedRoles.includes(uRole)) { + throw new HttpExceptions(`Adding the \'${uRole}\' role is not allowed`, 422); + } + }); + + return true; +}; + export default { checkTeamV3Permissions, checkIfAdmin, @@ -225,4 +272,7 @@ export default { getTeamName, checkUserAuthorization, checkIfLastManager, + getAllRolesForApproverUser, + listOfRolesAllowed, + checkAllowNewRoles, } \ No newline at end of file From a2918cd3f38b795d6c84a67416c06edd1f1cb7af Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 30 Jan 2023 12:05:32 +0000 Subject: [PATCH 076/229] update list of roles --- src/resources/utilities/constants.util.js | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index b2f038c6..6325de1b 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -355,6 +355,12 @@ const _roleMemberTeam = { CUST_DAR_MANAGER: 'custodian.dar.manager', CUST_DAR_REVIEWER: 'custodian.dar.reviewer', CUST_DUR_MANAGER: 'custodian.dur.manager', + HDRUK_ADMIN: 'hdruk.admin', + HDRUK_ONBOARDING_ADMIN: 'hdruk.onboarding.admin', + HDRUK_METADATA_ADMIN: 'hdruk.metadata.admin', + HDRUK_DAR_ADMIN: 'hdruk.dar.admin', + HDRUK_DUR_ADMIN: 'hdruk.dur.admin', + HDRUK_ENTITY_ADMIN: 'hdruk.entity.admin', }; const _rolesAcceptedByRoles = { @@ -387,6 +393,24 @@ const _rolesAcceptedByRoles = { 'custodian.dur.manager': [ 'custodian.dur.manager', ], + 'hdruk.admin': [ + 'hdruk.admin', + ], + 'hdruk.onboarding.admin': [ + 'hdruk.onboarding.admin', + ], + 'hdruk.metadata.admin': [ + 'hdruk.metadata.admin', + ], + 'hdruk.dar.admin': [ + 'hdruk.dar.admin', + ], + 'hdruk.dur.admin': [ + 'hdruk.dur.admin', + ], + 'hdruk.entity.admin': [ + 'hdruk.entity.admin', + ], }; export default { From eff980f112326872a9073b44e6f381725393f32b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 30 Jan 2023 13:26:46 +0000 Subject: [PATCH 077/229] update permissions --- src/resources/utilities/constants.util.js | 34 ----------------------- 1 file changed, 34 deletions(-) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index 6325de1b..d6081ef9 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -351,16 +351,8 @@ const _roleMemberTeam = { CUST_TEAM_ADMIN: 'custodian.team.admin', CUST_MD_MANAGER: 'custodian.metadata.manager', CUST_MD_EDITOR: 'custodian.metadata.editor', - CUST_DEVELOPER: 'custodian.developer', CUST_DAR_MANAGER: 'custodian.dar.manager', CUST_DAR_REVIEWER: 'custodian.dar.reviewer', - CUST_DUR_MANAGER: 'custodian.dur.manager', - HDRUK_ADMIN: 'hdruk.admin', - HDRUK_ONBOARDING_ADMIN: 'hdruk.onboarding.admin', - HDRUK_METADATA_ADMIN: 'hdruk.metadata.admin', - HDRUK_DAR_ADMIN: 'hdruk.dar.admin', - HDRUK_DUR_ADMIN: 'hdruk.dur.admin', - HDRUK_ENTITY_ADMIN: 'hdruk.entity.admin', }; const _rolesAcceptedByRoles = { @@ -368,10 +360,8 @@ const _rolesAcceptedByRoles = { 'custodian.team.admin', 'custodian.metadata.manager', 'custodian.metadata.editor', - 'custodian.developer', 'custodian.dar.manager', 'custodian.dar.reviewer', - 'custodian.dur.manager', ], 'custodian.metadata.manager': [ 'custodian.metadata.manager', @@ -380,9 +370,6 @@ const _rolesAcceptedByRoles = { 'custodian.metadata.editor': [ 'custodian.metadata.editor', ], - 'custodian.developer': [ - 'custodian.developer', - ], 'custodian.dar.manager': [ 'custodian.dar.manager', 'custodian.dar.reviewer', @@ -390,27 +377,6 @@ const _rolesAcceptedByRoles = { 'custodian.dar.reviewer': [ 'custodian.dar.reviewer', ], - 'custodian.dur.manager': [ - 'custodian.dur.manager', - ], - 'hdruk.admin': [ - 'hdruk.admin', - ], - 'hdruk.onboarding.admin': [ - 'hdruk.onboarding.admin', - ], - 'hdruk.metadata.admin': [ - 'hdruk.metadata.admin', - ], - 'hdruk.dar.admin': [ - 'hdruk.dar.admin', - ], - 'hdruk.dur.admin': [ - 'hdruk.dur.admin', - ], - 'hdruk.entity.admin': [ - 'hdruk.entity.admin', - ], }; export default { From 6e80e7dae116f01d6b6d890b207711ccbee299d8 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Mon, 30 Jan 2023 14:58:17 +0000 Subject: [PATCH 078/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5eb312c6..95e94cdd 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 \ No newline at end of file +v0.0.1 \ No newline at end of file From 299b10b11822aca1a94f56c7a4fbcad4c763a919 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Mon, 30 Jan 2023 15:39:47 +0000 Subject: [PATCH 079/229] GAT-1887: Pointing to dev branch --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 40e2ba26..7783a234 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -22,7 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: GAT-1887 + ref: dev - name: Read VERSION file id: getversion @@ -107,7 +107,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: GAT-1887 + ref: dev - name: Google Auth id: auth From f01fd9f57be9df288ff2cb1dde30a095d19bbf59 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:57:50 +0000 Subject: [PATCH 080/229] GAT-1887: Testing the dev pipeline from dev branch --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 95e94cdd..ae39fab3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 \ No newline at end of file +v0.0.0 From 878e5b46876e9ebfb5d0372666ae379a291265ef Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 30 Jan 2023 16:16:17 +0000 Subject: [PATCH 081/229] update list of roles --- src/resources/utilities/constants.util.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index d6081ef9..9a77720f 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -350,32 +350,32 @@ const _searchDataTypes = { const _roleMemberTeam = { CUST_TEAM_ADMIN: 'custodian.team.admin', CUST_MD_MANAGER: 'custodian.metadata.manager', - CUST_MD_EDITOR: 'custodian.metadata.editor', + CUST_MD_EDITOR: 'metadata_editor', CUST_DAR_MANAGER: 'custodian.dar.manager', - CUST_DAR_REVIEWER: 'custodian.dar.reviewer', + CUST_DAR_REVIEWER: 'reviewer', }; const _rolesAcceptedByRoles = { 'custodian.team.admin': [ 'custodian.team.admin', 'custodian.metadata.manager', - 'custodian.metadata.editor', + 'metadata_editor', 'custodian.dar.manager', - 'custodian.dar.reviewer', + 'reviewer', ], 'custodian.metadata.manager': [ 'custodian.metadata.manager', - 'custodian.metadata.editor', + 'metadata_editor', ], - 'custodian.metadata.editor': [ - 'custodian.metadata.editor', + 'metadata_editor': [ + 'metadata_editor', ], 'custodian.dar.manager': [ 'custodian.dar.manager', - 'custodian.dar.reviewer', + 'reviewer', ], - 'custodian.dar.reviewer': [ - 'custodian.dar.reviewer', + 'reviewer': [ + 'reviewer', ], }; From 44f5584844499c70fecc930b59a5efa413e539d1 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Mon, 30 Jan 2023 16:50:46 +0000 Subject: [PATCH 082/229] GAT-1887: Creating dependencies --- .github/workflows/dev_deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 7783a234..88d1d4dc 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -11,6 +11,7 @@ env: jobs: build: + needs: analyze # Add 'id-token' with the intended permissions for workload identity federation permissions: contents: write From 6cdc2f2609848386a60f845006714b3987efa16a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 30 Jan 2023 17:01:45 +0000 Subject: [PATCH 083/229] update error code --- src/resources/utilities/team.v3.util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 4b1aaed3..a3767168 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -257,7 +257,7 @@ const listOfRolesAllowed = (userRoles, rolesAcceptedByRoles) => { const checkAllowNewRoles = (userUpdateRoles, allowedRoles) => { userUpdateRoles.forEach(uRole => { if (!allowedRoles.includes(uRole)) { - throw new HttpExceptions(`Adding the \'${uRole}\' role is not allowed`, 422); + throw new HttpExceptions(`Adding the \'${uRole}\' role is not allowed`, 403); } }); From 9f22a943ad92827e15a29034fb5b4fa298eed555 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 1 Feb 2023 14:09:31 +0000 Subject: [PATCH 084/229] update permissions for team endpoints --- src/resources/team/v3/team.controller.js | 14 +++++++++---- .../checkingUserAuthorization.mock.js | 20 +++++++++++++++++++ .../checkingUserAuthorization.test.js | 19 ++++++++++++++++++ src/resources/utilities/team.v3.util.js | 11 ++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/resources/utilities/__mocks__/checkingUserAuthorization.mock.js create mode 100644 src/resources/utilities/__tests__/checkingUserAuthorization.test.js diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 1a2f7e7d..00fa4047 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -32,10 +32,13 @@ class TeamController extends TeamService { const deleteUserId = req.params.memberid; const userObj = req.user; const currentUserId = req.user._id; + const userTeams = userObj.teams || []; + const arrayOfAllowedRoles= ['custodian.team.admin']; - const team = await this.getTeamByTeamId(teamId); + const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + teamV3Util.checkingUserAuthorization(arrayOfAllowedRoles, currentUserRoles); - teamV3Util.checkUserAuthorization(currentUserId, 'manager', team, userObj); + const team = await this.getTeamByTeamId(teamId); let { members = [], users = [] } = team; @@ -69,10 +72,13 @@ class TeamController extends TeamService { const userObj = req.user; const currentUserId = req.user._id; const { memberId, roles = [] } = req.body; + const userTeams = userObj.teams || []; + const arrayOfAllowedRoles = ['custodian.team.admin']; - const team = await this.getTeamByTeamId(teamId); + const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + teamV3Util.checkingUserAuthorization(arrayOfAllowedRoles, currentUserRoles); - teamV3Util.checkUserAuthorization(currentUserId, 'manager', team, userObj); + const team = await this.getTeamByTeamId(teamId); let { members } = team; diff --git a/src/resources/utilities/__mocks__/checkingUserAuthorization.mock.js b/src/resources/utilities/__mocks__/checkingUserAuthorization.mock.js new file mode 100644 index 00000000..c9e5aa55 --- /dev/null +++ b/src/resources/utilities/__mocks__/checkingUserAuthorization.mock.js @@ -0,0 +1,20 @@ +const mockArrayRolesAllow = [ + "custodian.team.admin", +]; +const mockArrayRolesNotAllow = [ + "reviewer", +]; +const mockArrayCurrentUserRoles = [ + "admin_dataset", + "admin_data_use", + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager", +]; + +export { + mockArrayRolesAllow, + mockArrayRolesNotAllow, + mockArrayCurrentUserRoles +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkingUserAuthorization.test.js b/src/resources/utilities/__tests__/checkingUserAuthorization.test.js new file mode 100644 index 00000000..fa57b533 --- /dev/null +++ b/src/resources/utilities/__tests__/checkingUserAuthorization.test.js @@ -0,0 +1,19 @@ +import teamV3Util from '../team.v3.util'; +import { mockArrayRolesAllow, mockArrayRolesNotAllow, mockArrayCurrentUserRoles } from '../__mocks__/checkingUserAuthorization.mock'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; + +describe('checkingUserAuthorization test', () => { + it('should return true', () => { + let response = teamV3Util.checkingUserAuthorization(mockArrayRolesAllow, mockArrayCurrentUserRoles); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return an exception', () => { + try { + teamV3Util.checkingUserAuthorization(mockArrayRolesNotAllow, mockArrayCurrentUserRoles); + } catch (error) { + expect(error).toBeInstanceOf(HttpExceptions); + } + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index a3767168..a55976bf 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -211,6 +211,16 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { return true; }; +const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { + const allow = arrayCurrentUserRoles.filter(element => arrayRolesAllow.includes(element)).length; + + if (!allow) { + throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); + } + + return true; +}; + const checkIfLastManager = (members, deleteUserId) => { let managerCount = members.filter(mem => mem.roles.includes('manager') && mem.memberid.toString() !== deleteUserId).length; if (managerCount === 0) { @@ -271,6 +281,7 @@ export default { createTeamNotifications, getTeamName, checkUserAuthorization, + checkingUserAuthorization, checkIfLastManager, getAllRolesForApproverUser, listOfRolesAllowed, From a1b9e232da039bcc5b548ea8b35403815b2d66af Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 1 Feb 2023 16:13:08 +0000 Subject: [PATCH 085/229] added logging to google --- src/resources/team/v3/team.controller.js | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 00fa4047..e5047d7b 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -5,10 +5,15 @@ import teamV3Util from '../../utilities/team.v3.util'; import constants from '../../utilities/constants.util'; import HttpExceptions from '../../../exceptions/HttpExceptions'; import { UserModel } from '../../user/user.model'; +import { LoggingService } from '../../../services'; class TeamController extends TeamService { + _logger; + constructor() { super(); + + this._logger = new LoggingService(); } async getTeamMembers(req, res) { @@ -22,6 +27,15 @@ class TeamController extends TeamService { let members = teamV3Util.formatTeamMembers(team); + this.sendLogInGoogle({ + action: 'getTeamMembers', + input: { + teamId, + currentUserId, + }, + output: members, + }); + res.status(200).json({ members, }); @@ -57,6 +71,17 @@ class TeamController extends TeamService { } else { let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); + + this.sendLogInGoogle({ + action: 'deleteTeamMember', + input: { + teamId, + memberid: deleteUserId, + currentUserId, + }, + output: 'success' + }); + return res.status(204).json({ success: true, }); @@ -102,6 +127,17 @@ class TeamController extends TeamService { teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); + + this.sendLogInGoogle({ + action: 'addTeamMember', + input: { + teamId, + currentUserId, + body: req.body, + }, + output: users, + }); + return res.status(201).json({ success: true, members: users, @@ -144,6 +180,18 @@ class TeamController extends TeamService { } else { let updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); + + this.sendLogInGoogle({ + action: 'updateTeamMember', + input: { + teamId, + memberid: updateUserId, + currentUserId, + body: req.body, + }, + output: users, + }); + return res.json({ success: true, members: users, @@ -154,6 +202,13 @@ class TeamController extends TeamService { throw new HttpExceptions(e.message); } } + + sendLogInGoogle(message) { + const loggingEnabled = parseInt(process.env.LOGGING_LOG_ENABLED) || 0; + if (loggingEnabled) { + this._logger.sendDataInLogging(JSON.stringify(message), 'INFO'); + } + } } module.exports = new TeamController(); From c672af1d8db0de69ef93eeccbed2132ab53f0af3 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 2 Feb 2023 11:21:44 +0000 Subject: [PATCH 086/229] added middleware for team routes who check user roles for authorization --- src/middlewares/checkAccessTeamMiddleware.js | 20 +++++++++++++ src/middlewares/spatialFilterMiddleware.js | 0 src/resources/team/v3/team.controller.js | 11 ------- src/resources/team/v3/team.route.js | 30 ++++++++++++++++---- src/resources/utilities/team.v3.util.js | 2 +- 5 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 src/middlewares/checkAccessTeamMiddleware.js delete mode 100644 src/middlewares/spatialFilterMiddleware.js diff --git a/src/middlewares/checkAccessTeamMiddleware.js b/src/middlewares/checkAccessTeamMiddleware.js new file mode 100644 index 00000000..4fa12035 --- /dev/null +++ b/src/middlewares/checkAccessTeamMiddleware.js @@ -0,0 +1,20 @@ +import HttpExceptions from '../exceptions/HttpExceptions'; +import teamV3Util from '../resources/utilities/team.v3.util'; + +const checkAccessToTeamMiddleware = (arrayAllowedPermissions) => (req, res, next) => { + const teamId = req.params.teamid || ''; + const currentUserId = req.user._id || ''; + const userTeams = req.user.teams || []; + + if (!teamId || !currentUserId) { + throw new HttpExceptions('One or more required parameters missing', 400); + } + + + const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + teamV3Util.checkingUserAuthorization(arrayAllowedPermissions, currentUserRoles); + + next(); +} + +export { checkAccessToTeamMiddleware }; \ No newline at end of file diff --git a/src/middlewares/spatialFilterMiddleware.js b/src/middlewares/spatialFilterMiddleware.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index e5047d7b..7aa37430 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -46,11 +46,6 @@ class TeamController extends TeamService { const deleteUserId = req.params.memberid; const userObj = req.user; const currentUserId = req.user._id; - const userTeams = userObj.teams || []; - const arrayOfAllowedRoles= ['custodian.team.admin']; - - const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - teamV3Util.checkingUserAuthorization(arrayOfAllowedRoles, currentUserRoles); const team = await this.getTeamByTeamId(teamId); @@ -94,14 +89,8 @@ class TeamController extends TeamService { async addTeamMember(req, res) { const teamId = req.params.teamid; - const userObj = req.user; const currentUserId = req.user._id; const { memberId, roles = [] } = req.body; - const userTeams = userObj.teams || []; - const arrayOfAllowedRoles = ['custodian.team.admin']; - - const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - teamV3Util.checkingUserAuthorization(arrayOfAllowedRoles, currentUserRoles); const team = await this.getTeamByTeamId(teamId); diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index 3968dfac..ec7a09c5 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -1,6 +1,7 @@ import express from 'express'; import passport from 'passport'; - +import { checkAccessToTeamMiddleware } from '../../../middlewares/checkAccessTeamMiddleware'; +import constants from '../../utilities/constants.util'; const TeamController = require('./team.controller'); const router = express.Router(); @@ -8,22 +9,39 @@ const router = express.Router(); // @route GET api/v3/teams/:teamid/members // @desc GET all team members for team // @access Private -router.get('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.getTeamMembers(req, res)); +router.get( + '/:teamid/members', + passport.authenticate('jwt'), + (req, res) => TeamController.getTeamMembers(req, res) +); // @route DELETE api/v3/teams/:teamid/members/:memberid // @desc DELETE team member from the team // @access Private -router.delete('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.deleteTeamMember(req, res)); - +router.delete( + '/:teamid/members/:memberid', + passport.authenticate('jwt'), + checkAccessToTeamMiddleware([constants.roleMemberTeam.CUST_TEAM_ADMIN]), + (req, res) => TeamController.deleteTeamMember(req, res), +); // @route POST api/v3/teams/:teamid/members // @desc POST add new team member // @access Private -router.post('/:teamid/members', passport.authenticate('jwt'), (req, res) => TeamController.addTeamMember(req, res)); +router.post( + '/:teamid/members', + passport.authenticate('jwt'), + checkAccessToTeamMiddleware([constants.roleMemberTeam.CUST_TEAM_ADMIN]), + (req, res) => TeamController.addTeamMember(req, res), +); // @route PATCH api/v3/teams/:teamid/members/:memberid // @desc PATCH add new team member // @access Private -router.patch('/:teamid/members/:memberid', passport.authenticate('jwt'), (req, res) => TeamController.updateTeamMember(req, res)); +router.patch( + '/:teamid/members/:memberid', + passport.authenticate('jwt'), + (req, res) => TeamController.updateTeamMember(req, res), +); module.exports = router; \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index a55976bf..ce87c210 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -213,7 +213,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { const allow = arrayCurrentUserRoles.filter(element => arrayRolesAllow.includes(element)).length; - + if (!allow) { throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); } From 620c1254698043e29ac0a8f46c644ee9c2e852f4 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 3 Feb 2023 13:54:16 +0000 Subject: [PATCH 087/229] DAR Manager --- Dockerfile.dev | 2 +- .../datarequest/datarequest.controller.js | 460 +++++------------- .../datarequest/utils/datarequest.util.js | 65 +-- .../publisher/publisher.controller.js | 69 +-- src/resources/utilities/team.v3.util.js | 25 + 5 files changed, 204 insertions(+), 417 deletions(-) diff --git a/Dockerfile.dev b/Dockerfile.dev index f0ad3803..c3e780a0 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:16 +FROM node:18 ENV GOOGLE_APPLICATION_CREDENTIALS="/usr/local/etc/gcloud/application_default_credentials.json" diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 2f4cb631..a3afc8a5 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -3,6 +3,7 @@ import moment from 'moment'; import mongoose from 'mongoose'; import teamController from '../team/team.controller'; +import teamV3Util from '../utilities/team.v3.util'; import datarequestUtil from './utils/datarequest.util'; import notificationBuilder from '../utilities/notificationBuilder'; import emailGenerator from '../utilities/emailGenerator.util'; @@ -17,6 +18,7 @@ import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; import { dataUseRegisterController } from '../dataUseRegister/dependency'; import { publishMessageWithRetryToPubSub } from '../../services/google/PubSubWithRetryService'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const logCategory = 'Data Access Request'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); @@ -83,12 +85,8 @@ export default class DataRequestController extends Controller { canViewSubmitted: true, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred searching for user applications', - }); + throw new HttpExceptions(`An error occurred searching for user applications`, 500); } } @@ -107,7 +105,7 @@ export default class DataRequestController extends Controller { // 2. Find the matching record and include attached datasets records with publisher details and workflow details let accessRecord = await this.dataRequestService.getApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'The application could not be found.' }); + throw new HttpExceptions(`The application could not be found.`, 404); } // 3. If invalid version requested, return 404 @@ -116,7 +114,7 @@ export default class DataRequestController extends Controller { requestedVersion ); if (!isValidVersion) { - return res.status(404).json({ status: 'error', message: 'The requested application version could not be found.' }); + throw new HttpExceptions(`The requested application version could not be found.`, 404); } // 4. Get requested amendment iteration details @@ -126,14 +124,11 @@ export default class DataRequestController extends Controller { ); // 5. Check if requesting user is custodian member or applicant/contributor - const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + const userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } // 6. Set edit mode for applicants who have not yet submitted const { applicationStatus, jsonSchema, versionTree, applicationType } = accessRecord; @@ -222,12 +217,8 @@ export default class DataRequestController extends Controller { }, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred opening this data access request application', - }); + throw new HttpExceptions(`An error occurred opening this data access request application`, 500); } } @@ -262,7 +253,7 @@ export default class DataRequestController extends Controller { return await this.getAccessRequestById(req, res); } else { if (_.isEmpty(datasets)) { - return res.status(500).json({ status: 'error', message: 'No datasets available.' }); + throw new HttpExceptions(`No datasets available.`, 500); } const { datasetfields: { publisher = '' }, @@ -321,12 +312,8 @@ export default class DataRequestController extends Controller { }, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred opening a data access request application for the requested dataset(s)', - }); + throw new HttpExceptions(`An error occurred opening a data access request application for the requested dataset(s)`, 500); } } @@ -347,12 +334,8 @@ export default class DataRequestController extends Controller { }), }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred populating additional information for contributors.', - }); + throw new HttpExceptions(`An error occurred populating additional information for contributors.`, 500); } } @@ -377,14 +360,11 @@ export default class DataRequestController extends Controller { } // 3. Check user type and authentication to submit application - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } // 4. Ensure single datasets are mapped correctly into array (backward compatibility for single dataset applications) if (_.isEmpty(accessRecord.datasets)) { @@ -427,10 +407,7 @@ export default class DataRequestController extends Controller { // 6. Ensure a valid submission is taking place if (_.isNil(accessRecord.applicationType)) { - return res.status(400).json({ - status: 'error', - message: 'Application cannot be submitted as it has reached a final decision status.', - }); + throw new HttpExceptions(`Application cannot be submitted as it has reached a final decision status.`, 400); } // 7. Save changes to db @@ -488,15 +465,10 @@ export default class DataRequestController extends Controller { } } - // 11. Return aplication and successful response return res.status(200).json({ status: 'success', data: savedAccessRecord }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred submitting the application', - }); + throw new HttpExceptions(`An error occurred submitting the application`, 500); } } @@ -518,7 +490,7 @@ export default class DataRequestController extends Controller { let accessRecord = await this.dataRequestService.getApplicationToUpdateById(id); // 4. Check access record if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Data Access Request not found.' }); + throw new HttpExceptions(`Data Access Request not found.`, 404); } // 5. Update record object accessRecord = await this.dataRequestService.updateApplication(accessRecord, updateObj).catch(err => { @@ -580,12 +552,8 @@ export default class DataRequestController extends Controller { jsonSchema: dirtySchema ? accessRecord.jsonSchema : undefined, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application', - }); + throw new HttpExceptions(`An error occurred updating the application`, 500); } } @@ -607,40 +575,30 @@ export default class DataRequestController extends Controller { // 3. Find the relevant data request application let accessRecord = await this.dataRequestService.getApplicationWithWorkflowById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 4. Check if the user is permitted to perform update to application let isDirty = false, statusChange = false, contributorChange = false; - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord.toObject(), requestingUserId, requestingUserObjectId ); - if (!authorised) { - return res.status(401).json({ - status: 'error', - message: 'Unauthorised to perform this update.', - }); - } - let { authorIds: currentAuthors } = accessRecord; let newAuthors = []; // 5. Extract new application status and desc to save updates if (userType === constants.userTypes.CUSTODIAN) { // Only a custodian manager can set the final status of an application - authorised = false; const { team = {} } = accessRecord.publisherObj.toObject(); if (!_.isEmpty(team)) { - authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserObjectId); - } - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserObjectId); } + // Extract params from body ({ applicationStatus, applicationStatusDesc } = req.body); const finalStatuses = [ @@ -791,12 +749,8 @@ export default class DataRequestController extends Controller { data: accessRecord._doc, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application', - }); + throw new HttpExceptions(`An error occurred updating the application`, 500); } } @@ -815,23 +769,20 @@ export default class DataRequestController extends Controller { const appToDelete = await this.dataRequestService.getApplicationWithTeamById(appIdToDelete, { lean: true }); // 3. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( appToDelete, requestingUserId, requestingUserObjectId ); // 4. Return unauthorised message if the requesting user is not an applicant - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } // 5. If application is not in progress, actions cannot be performed if (appToDelete.applicationStatus !== constants.applicationStatuses.INPROGRESS) { - return res.status(400).json({ - success: false, - message: 'This application is no longer in pre-submission status and therefore this action cannot be performed', - }); + throw new HttpExceptions(`This application is no longer in pre-submission status and therefore this action cannot be performed`, 400); } // 6. Delete application @@ -846,12 +797,8 @@ export default class DataRequestController extends Controller { success: true, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred deleting the existing application', - }); + throw new HttpExceptions(`An error occurred deleting the existing application`, 500); } } @@ -874,24 +821,24 @@ export default class DataRequestController extends Controller { let appToClone = await this.dataRequestService.getApplicationWithTeamById(id, { lean: true }); if (!appToClone) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. If invalid version requested to clone, return 404 const { isValidVersion, requestedMinorVersion } = this.dataRequestService.validateRequestedVersion(appToClone, requestedVersion); if (!isValidVersion) { - return res.status(404).json({ status: 'error', message: 'The requested application version could not be found.' }); + throw new HttpExceptions(`The requested application version could not be found.`, 404); } // 4. Get requested amendment iteration details const { versionIndex } = this.amendmentService.getAmendmentIterationDetailsByVersion(appToClone, requestedMinorVersion); // 5. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication(appToClone, requestingUserId, requestingUserObjectId); + let userType = datarequestUtil.getUserPermissionsForApplication(appToClone, requestingUserId, requestingUserObjectId); // 6. Return unauthorised message if the requesting user is not an applicant - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } // 7. Update question answers with modifications since original submission @@ -914,16 +861,16 @@ export default class DataRequestController extends Controller { const appToCloneInto = await this.dataRequestService.getApplicationWithTeamById(appIdToCloneInto, { lean: true }); // Ensure application to clone into was found if (!appToCloneInto) { - return res.status(404).json({ status: 'error', message: 'Application to clone into not found.' }); + throw new HttpExceptions(`Application to clone into not found.`, 404); } // Get permissions for application to clone into - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( appToCloneInto, requestingUserId, requestingUserObjectId ); // Return unauthorised message if the requesting user is not authorised to the new application - if (!authorised || userType !== constants.userTypes.APPLICANT) { + if (userType !== constants.userTypes.APPLICANT) { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } clonedAccessRecord = await datarequestUtil.cloneIntoExistingApplication(appToClone, appToCloneInto); @@ -949,12 +896,8 @@ export default class DataRequestController extends Controller { accessRecord: clonedAccessRecord, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred cloning the existing application', - }); + throw new HttpExceptions(`An error occurred cloning the existing application.`, 500); } } @@ -969,34 +912,29 @@ export default class DataRequestController extends Controller { const requestingUserObjectId = req.user._id; let { questionId, questionSetId, questionIds = [], mode, separatorText = '' } = req.body; if (_.isEmpty(questionId) || _.isEmpty(questionSetId)) { - return res.status(400).json({ - success: false, - message: 'You must supply the unique identifiers for the question to perform an action', - }); + throw new HttpExceptions(`You must supply the unique identifiers for the question to perform an action`, 400); } // 2. Retrieve DAR from database let accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. If application is not in progress, actions cannot be performed if (accessRecord.applicationStatus !== constants.applicationStatuses.INPROGRESS) { - return res.status(400).json({ - success: false, - message: 'This application is no longer in pre-submission status and therefore this action cannot be performed', - }); + throw new HttpExceptions(`This application is no longer in pre-submission status and therefore this action cannot be performed`, 400); } // 4. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord.toObject(), requestingUserId, requestingUserObjectId ); // 5. Return unauthorised message if the requesting user is not an applicant - if (!authorised || userType !== constants.userTypes.APPLICANT) { + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } @@ -1015,30 +953,21 @@ export default class DataRequestController extends Controller { break; case constants.formActions.ADDREPEATABLEQUESTIONS: if (_.isEmpty(questionIds)) { - return res.status(400).json({ - success: false, - message: 'You must supply the question identifiers to duplicate when performing this action', - }); + throw new HttpExceptions(`You must supply the question identifiers to duplicate when performing this action`, 400); } const duplicateQuestions = dynamicForm.duplicateQuestions(questionSetId, questionIds, separatorText, jsonSchema); jsonSchema = dynamicForm.insertQuestions(questionSetId, questionId, duplicateQuestions, jsonSchema); break; case constants.formActions.REMOVEREPEATABLEQUESTIONS: if (_.isEmpty(questionIds)) { - return res.status(400).json({ - success: false, - message: 'You must supply the question identifiers to remove when performing this action', - }); + throw new HttpExceptions(`You must supply the question identifiers to remove when performing this action`, 400); } questionIds = [...questionIds, questionId]; jsonSchema = dynamicForm.removeQuestionReferences(questionSetId, questionIds, jsonSchema); questionAnswers = dynamicForm.removeQuestionAnswers(questionIds, questionAnswers); break; default: - return res.status(400).json({ - success: false, - message: 'You must supply a valid action to perform', - }); + throw new HttpExceptions(`You must supply a valid action to perform`, 400); } // 8. Update record @@ -1068,12 +997,8 @@ export default class DataRequestController extends Controller { }, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application amendment', - }); + throw new HttpExceptions(`An error occurred updating the application amendment`, 500); } } @@ -1092,31 +1017,29 @@ export default class DataRequestController extends Controller { // 2. Find the matching record and include attached datasets records with publisher details and workflow details let accessRecord = await this.dataRequestService.getApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'The application could not be found.' }); + throw new HttpExceptions(`The application could not be found.`, 404); } // 3. Check if requesting user is custodian member or applicant/contributor - const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + const userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } // 4. If invalid version requested, return 404 const { isValidVersion, requestedMinorVersion } = this.dataRequestService.validateRequestedVersion(accessRecord, requestedVersion); if (!isValidVersion) { - return res.status(404).json({ status: 'error', message: 'The requested application version could not be found.' }); + throw new HttpExceptions(`The requested application version could not be found.`, 404); } // 5. Check version is the latest version const { isLatestMinorVersion } = this.amendmentService.getAmendmentIterationDetailsByVersion(accessRecord, requestedMinorVersion); if (!isLatestMinorVersion) { - return res - .status(400) - .json({ status: 'error', message: 'This action can only be performed against the latest version of an approved application' }); + throw new HttpExceptions(`This action can only be performed against the latest version of an approved application`, 400); } // 6. Check application is in correct status @@ -1125,9 +1048,7 @@ export default class DataRequestController extends Controller { applicationStatus !== constants.applicationStatuses.APPROVED && applicationStatus !== constants.applicationStatuses.APPROVEDWITHCONDITIONS ) { - return res - .status(400) - .json({ status: 'error', message: 'This action can only be performed against an application that has been approved' }); + throw new HttpExceptions(`This action can only be performed against an application that has been approved`, 400); } // 7. Update question answers with modifications since original submission (minor version updates) @@ -1139,7 +1060,7 @@ export default class DataRequestController extends Controller { }); if (!newAccessRecord) { - return res.status(400).json({ status: 'error', message: 'Creating application amendment failed' }); + throw new HttpExceptions(`Creating application amendment failed`, 400); } // 9. Get amended application (new major version) with all details populated @@ -1154,12 +1075,8 @@ export default class DataRequestController extends Controller { }, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred opening this data access request application', - }); + throw new HttpExceptions(`An error occurred opening this data access request application`, 500); } } @@ -1181,17 +1098,14 @@ export default class DataRequestController extends Controller { // 4. Get access record let accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 5. Check if requesting user is custodian member or applicant/contributor - let { authorised } = datarequestUtil.getUserPermissionsForApplication(accessRecord, requestingUserId, requestingUserObjectId); - // 6. Check authorisation - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } + datarequestUtil.getUserPermissionsForApplication(accessRecord, requestingUserId, requestingUserObjectId); + // 7. Check files if (_.isEmpty(files)) { - return res.status(400).json({ status: 'error', message: 'No files to upload' }); + throw new HttpExceptions(`No files to upload`, 400); } // 8. Upload files const mediaFiles = await this.dataRequestService @@ -1202,12 +1116,8 @@ export default class DataRequestController extends Controller { // 9. return response return res.status(200).json({ status: 'success', mediaFiles }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred uploading the file to the application', - }); + throw new HttpExceptions(`An error occurred uploading the file to the application`, 500); } } @@ -1222,7 +1132,7 @@ export default class DataRequestController extends Controller { // 2. get AccessRecord const accessRecord = await this.dataRequestService.getFilesForApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. get file @@ -1232,12 +1142,8 @@ export default class DataRequestController extends Controller { // 4. Return successful response return res.status(200).json({ status: accessRecord.files[fileIndex].status }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred attempting to retrieve the status of an uploaded file', - }); + throw new HttpExceptions(`An error occurred attempting to retrieve the status of an uploaded file`, 500); } } @@ -1252,7 +1158,7 @@ export default class DataRequestController extends Controller { // 2. Get AccessRecord const accessRecord = await this.dataRequestService.getFilesForApplicationById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. Find the file in the files array from db const mediaFile = @@ -1262,10 +1168,7 @@ export default class DataRequestController extends Controller { }) || {}; // 4. No file return if (_.isEmpty(mediaFile)) { - return res.status(400).json({ - status: 'error', - message: 'No file to download, please try again later', - }); + throw new HttpExceptions(`No file to download, please try again later`, 400); } // 6. get the name of the file let { name, fileId: dbFileId } = mediaFile; @@ -1275,12 +1178,8 @@ export default class DataRequestController extends Controller { // 8. send file back to user return res.status(200).sendFile(`${process.env.TMPDIR}${initialApplicationId}/${dbFileId}_${name}`); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred attempting to retrieve an uploaded file', - }); + throw new HttpExceptions(`An error occurred attempting to retrieve an uploaded file`, 500); } } @@ -1298,7 +1197,7 @@ export default class DataRequestController extends Controller { const accessRecord = await this.dataRequestService.getFilesForApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } //3. Check the status is valid @@ -1308,7 +1207,7 @@ export default class DataRequestController extends Controller { status !== fileStatus.ERROR && status !== fileStatus.QUARANTINED ) { - return res.status(400).json({ status: 'error', message: 'File status not valid' }); + throw new HttpExceptions(`File status not valid`, 400); } //4. Update all versions of application using version tree @@ -1318,12 +1217,8 @@ export default class DataRequestController extends Controller { success: true, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred attempting to update the status of an uploaded file', - }); + throw new HttpExceptions(`An error occurred attempting to update the status of an uploaded file`, 500); } } @@ -1343,27 +1238,24 @@ export default class DataRequestController extends Controller { const accessRecord = await this.dataRequestService.getFilesForApplicationById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. If application is not in progress, actions cannot be performed if (accessRecord.applicationStatus !== constants.applicationStatuses.INPROGRESS) { - return res.status(400).json({ - success: false, - message: 'This application is no longer in pre-submission status and therefore this action cannot be performed', - }); + throw new HttpExceptions(`This application is no longer in pre-submission status and therefore this action cannot be performed`, 400); } // 4. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord.toObject(), requestingUserId, requestingUserObjectId ); // 5. Return unauthorised message if the requesting user is not an applicant - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } // 6. Remove the file from the application @@ -1379,7 +1271,7 @@ export default class DataRequestController extends Controller { return res.status(200).json({ status: 'success' }); } catch (err) { console.error(err.message); - res.status(500).json({ status: 'error', message: err.message }); + throw new HttpExceptions(err.message, 500); } } @@ -1396,55 +1288,37 @@ export default class DataRequestController extends Controller { const requestingUserObjectId = req.user._id; const { workflowId = '' } = req.body; if (_.isEmpty(workflowId)) { - return res.status(400).json({ - success: false, - message: 'You must supply the unique identifier to assign a workflow to this application', - }); + throw new HttpExceptions(`You must supply the unique identifier to assign a workflow to this application`, 400); } // 2. Retrieve DAR from database let accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. Check permissions of user is manager of associated team - let authorised = false; if (_.has(accessRecord.toObject(), 'publisherObj.team')) { let { team } = accessRecord.publisherObj; - authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team.toObject(), requestingUserObjectId); - } - - // 4. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); } // 5. Check publisher allows workflows const { workflowEnabled = false } = accessRecord.publisherObj; if (!workflowEnabled) { - return res.status(400).json({ - success: false, - message: 'This custodian has not enabled workflows', - }); + throw new HttpExceptions(`This custodian has not enabled workflows`, 400); } // 6. Check no workflow already assigned const { workflowId: currentWorkflowId = '' } = accessRecord; if (!_.isEmpty(currentWorkflowId)) { - return res.status(400).json({ - success: false, - message: 'This application already has a workflow assigned', - }); + throw new HttpExceptions(`This application already has a workflow assigned`, 400); } // 7. Check application is in-review const { applicationStatus } = accessRecord; if (applicationStatus !== constants.applicationStatuses.INREVIEW) { - return res.status(400).json({ - success: false, - message: 'The application status must be set to in review to assign a workflow', - }); + throw new HttpExceptions(`The application status must be set to in review to assign a workflow`, 400); } // 8. Assign workflow and save changes to application @@ -1478,12 +1352,8 @@ export default class DataRequestController extends Controller { success: true, }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred assigning the workflow', - }); + throw new HttpExceptions(`An error occurred assigning the workflow`, 500); } } @@ -1499,38 +1369,26 @@ export default class DataRequestController extends Controller { // 2. Retrieve DAR from database let accessRecord = await this.dataRequestService.getApplicationWithWorkflowById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. Check permissions of user is manager of associated team - let authorised = false; if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team.toObject(), requestingUserObjectId); - } - - // 4. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); } // 5. Check application is in review state const { applicationStatus } = accessRecord; if (applicationStatus !== constants.applicationStatuses.INREVIEW) { - return res.status(400).json({ - success: false, - message: 'The application status must be set to in review', - }); + throw new HttpExceptions(`The application status must be set to in review`, 400); } // 6. Check a workflow is assigned with valid steps const { workflow = {} } = accessRecord; const { steps = [] } = workflow; if (_.isEmpty(workflow) || _.isEmpty(steps)) { - return res.status(400).json({ - success: false, - message: 'A valid workflow has not been attached to this application', - }); + throw new HttpExceptions(`A valid workflow has not been attached to this application`, 400); } // 7. Get the attached active workflow step @@ -1538,10 +1396,7 @@ export default class DataRequestController extends Controller { return step.active === true; }); if (activeStepIndex === -1) { - return res.status(400).json({ - success: false, - message: 'There is no active step to override for this workflow', - }); + throw new HttpExceptions(`There is no active step to override for this workflow`, 400); } // 8. Update the step to be completed closing off end date/time @@ -1601,12 +1456,8 @@ export default class DataRequestController extends Controller { // 16. Return aplication and successful response return res.status(200).json({ status: 'success' }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred assigning the workflow', - }); + throw new HttpExceptions(`An error occurred assigning the workflow`, 500); } } @@ -1621,46 +1472,31 @@ export default class DataRequestController extends Controller { const requestingUserObjectId = req.user._id; const { approved, comments = '' } = req.body; if (_.isUndefined(approved) || _.isEmpty(comments)) { - return res.status(400).json({ - success: false, - message: 'You must supply the approved status with a reason', - }); + throw new HttpExceptions(`You must supply the approved status with a reason`, 400); } // 2. Retrieve DAR from database let accessRecord = await this.dataRequestService.getApplicationWithWorkflowById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 400); } // 3. Check permissions of user is reviewer of associated team - let authorised = false; if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - authorised = teamController.checkTeamPermissions(constants.roleTypes.REVIEWER, team.toObject(), requestingUserObjectId); - } - - // 4. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_REVIEWER], team.toObject(), requestingUserObjectId); } // 5. Check application is in-review const { applicationStatus } = accessRecord; if (applicationStatus !== constants.applicationStatuses.INREVIEW) { - return res.status(400).json({ - success: false, - message: 'The application status must be set to in review to cast a vote', - }); + throw new HttpExceptions(`The application status must be set to in review to cast a vote`, 400); } // 6. Ensure a workflow has been attached to this application const { workflow } = accessRecord; if (!workflow) { - return res.status(400).json({ - success: false, - message: 'There is no workflow attached to this application in order to cast a vote', - }); + throw new HttpExceptions(`There is no workflow attached to this application in order to cast a vote`, 400); } // 7. Ensure the requesting user is expected to cast a vote @@ -1669,10 +1505,7 @@ export default class DataRequestController extends Controller { return step.active === true; }); if (!steps[activeStepIndex].reviewers.map(reviewer => reviewer._id.toString()).includes(requestingUserObjectId.toString())) { - return res.status(400).json({ - success: false, - message: 'You have not been assigned to vote on this review phase', - }); + throw new HttpExceptions(`You have not been assigned to vote on this review phase`, 400); } //8. Ensure the requesting user has not already voted @@ -1682,10 +1515,7 @@ export default class DataRequestController extends Controller { return rec.reviewer.equals(requestingUserObjectId); }); if (found) { - return res.status(400).json({ - success: false, - message: 'You have already voted on this review phase', - }); + throw new HttpExceptions(`You have already voted on this review phase`, 400); } } @@ -1770,12 +1600,8 @@ export default class DataRequestController extends Controller { // 17. Return aplication and successful response return res.status(200).json({ status: 'success', data: accessRecord._doc }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred assigning the workflow', - }); + throw new HttpExceptions(`An error occurred assigning the workflow`, 500); } } @@ -1791,28 +1617,19 @@ export default class DataRequestController extends Controller { // 2. Retrieve DAR from database let accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: false }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. Check permissions of user is reviewer of associated team - let authorised = false; if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team.toObject(), requestingUserObjectId); - } - - // 4. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); } // 5. Check application is in submitted state const { applicationStatus } = accessRecord; if (applicationStatus !== constants.applicationStatuses.SUBMITTED) { - return res.status(400).json({ - success: false, - message: 'The application status must be set to submitted to start a review', - }); + throw new HttpExceptions(`The application status must be set to submitted to start a review`, 400); } // 6. Update application to 'in review' @@ -1857,12 +1674,8 @@ export default class DataRequestController extends Controller { // 12. Return aplication and successful response return res.status(200).json({ status: 'success' }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred assigning the workflow', - }); + throw new HttpExceptions(`An error occurred assigning the workflow`, 500); } } @@ -1877,14 +1690,11 @@ export default class DataRequestController extends Controller { // 2. Retrieve DAR from database const accessRecord = await this.dataRequestService.getApplicationWithWorkflowById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } const { workflow } = accessRecord; if (_.isEmpty(workflow)) { - return res.status(400).json({ - status: 'error', - message: 'There is no workflow attached to this application.', - }); + throw new HttpExceptions(`There is no workflow attached to this application.`, 400); } const activeStepIndex = workflow.steps.findIndex(step => { return step.active === true; @@ -1902,12 +1712,8 @@ export default class DataRequestController extends Controller { } return res.status(200).json({ status: 'success' }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred triggering notifications for workflow review deadlines', - }); + throw new HttpExceptions(`An error occurred triggering notifications for workflow review deadlines`, 500); } } @@ -1928,26 +1734,23 @@ export default class DataRequestController extends Controller { const accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: true }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. If application is not in progress, actions cannot be performed if (accessRecord.applicationStatus !== constants.applicationStatuses.INPROGRESS) { - return res.status(400).json({ - success: false, - message: 'This application is no longer in pre-submission status and therefore this action cannot be performed', - }); + throw new HttpExceptions(`This application is no longer in pre-submission status and therefore this action cannot be performed`, 400); } // 4. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); // 5. Return unauthorised message if the requesting user is not an applicant - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } // 6. Send notification to the authorised user @@ -1955,12 +1758,8 @@ export default class DataRequestController extends Controller { return res.status(200).json({ status: 'success' }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred emailing the application', - }); + throw new HttpExceptions(`An error occurred emailing the application`, 500); } } @@ -2968,16 +2767,16 @@ export default class DataRequestController extends Controller { let accessRecord = await this.dataRequestService.getApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'The application could not be found.' }); + throw new HttpExceptions(`The application could not be found.`, 404); } - const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + const userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised || userType !== constants.userTypes.APPLICANT) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + if (userType !== constants.userTypes.APPLICANT) { + throw new HttpExceptions(`Unauthorised`, 401); } await this.dataRequestService.shareApplication(accessRecord).catch(err => { @@ -2989,10 +2788,7 @@ export default class DataRequestController extends Controller { }); } catch (err) { logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application', - }); + throw new HttpExceptions(`An error occurred updating the application`, 500); } } @@ -3008,26 +2804,24 @@ export default class DataRequestController extends Controller { let accessRecord = await this.dataRequestService.getApplicationById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'The application could not be found.' }); + throw new HttpExceptions(`The application could not be found.`, 404); } - const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + const userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } else if ( + if ( userType === constants.userTypes.APPLICANT && ![constants.DARMessageTypes.DARNOTESAPPLICANT, constants.DARMessageTypes.DARMESSAGE].includes(messageType) ) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } else if ( userType === constants.userTypes.CUSTODIAN && ![constants.DARMessageTypes.DARNOTESCUSTODIAN, constants.DARMessageTypes.DARMESSAGE].includes(messageType) ) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } const topic = await this.topicService.getTopicForDAR(id, questionId || panelId, messageType); @@ -3050,10 +2844,7 @@ export default class DataRequestController extends Controller { }); } catch (err) { logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application', - }); + throw new HttpExceptions(`An error occurred updating the application`, 500); } } @@ -3070,27 +2861,25 @@ export default class DataRequestController extends Controller { let accessRecord = await this.dataRequestService.getApplicationWithTeamById(id, { lean: true }); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'The application could not be found.' }); + throw new HttpExceptions(`The application could not be found.`, 404); } - const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + const userType = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } else if ( + if ( userType === constants.userTypes.APPLICANT && ![constants.DARMessageTypes.DARNOTESAPPLICANT, constants.DARMessageTypes.DARMESSAGE].includes(messageType) ) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } else if ( userType === constants.userTypes.CUSTODIAN && ![constants.DARMessageTypes.DARNOTESCUSTODIAN, constants.DARMessageTypes.DARMESSAGE].includes(messageType) ) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } let topic = await this.topicService.getTopicForDAR(id, questionId || panel.panelId, messageType); @@ -3181,10 +2970,7 @@ export default class DataRequestController extends Controller { }); } catch (err) { logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application', - }); + throw new HttpExceptions(`An error occurred updating the application`, 500); } } } diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index b6020cdd..1faea00e 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -1,6 +1,6 @@ import { has, isEmpty, isNil } from 'lodash'; import constants from '../../utilities/constants.util'; -import teamController from '../../team/team.controller'; +import teamV3Util from '../../utilities/team.v3.util'; import moment from 'moment'; import { DataRequestSchemaModel } from '../schema/datarequest.schemas.model'; import dynamicForm from '../../utilities/dynamicForms/dynamicForm.util'; @@ -33,36 +33,45 @@ const injectQuestionActions = (jsonSchema, userType, applicationStatus, role = ' }; const getUserPermissionsForApplication = (application, userId, _id) => { - try { - let authorised = false, - isTeamMember = false, - userType = ''; - // Return default unauthorised with no user type if incorrect params passed - if (!application || !userId || !_id) { - return { authorised, userType }; - } - // Check if the user is a custodian team member and assign permissions if so - if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { - isTeamMember = teamController.checkTeamPermissions('', application.datasets[0].publisher.team, _id); - } else if (has(application, 'publisherObj.team')) { - isTeamMember = teamController.checkTeamPermissions('', application.publisherObj.team, _id); - } - if (isTeamMember && (application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { - userType = constants.userTypes.CUSTODIAN; + + let authorised = false, + userType = ''; + + if (!application || !userId || !_id) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } + + if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { + teamV3Util.checkUserRolesByTeam( + [constants.roleMemberTeam.CUST_DAR_MANAGER], + application.datasets[0].publisher.team, + _id + ); + } else if (has(application, 'publisherObj.team')) { + teamV3Util.checkUserRolesByTeam( + [constants.roleMemberTeam.CUST_DAR_MANAGER], + application.publisherObj.team, + _id + ); + } + + if ((application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { + userType = constants.userTypes.CUSTODIAN; + authorised = true; + } + + if (application.applicationStatus === constants.applicationStatuses.INPROGRESS || isEmpty(userType)) { + if (application.userId === userId || (application.authorIds && application.authorIds.includes(userId))) { + userType = constants.userTypes.APPLICANT; authorised = true; } - // If user is not authenticated as a custodian, check if they are an author or the main applicant - if (application.applicationStatus === constants.applicationStatuses.INPROGRESS || isEmpty(userType)) { - if (application.userId === userId || (application.authorIds && application.authorIds.includes(userId))) { - userType = constants.userTypes.APPLICANT; - authorised = true; - } - } - return { authorised, userType }; - } catch (err) { - console.error(err.message); - return { authorised: false, userType: '' }; } + + if (authorised) { + return userType; + } + + throw new HttpExceptions(`User not authorized to perform this action`, 403); }; const extractApplicantNames = questionAnswers => { diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 7b3c5cb5..33781ddf 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -3,6 +3,7 @@ import constants from '../utilities/constants.util'; import teamController from '../team/team.controller'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; +import teamV3Util from '../utilities/team.v3.util'; const logCategory = 'Publisher'; @@ -17,7 +18,6 @@ export default class PublisherController extends Controller { async getPublisher(req, res) { try { - // 1. Get the publisher from the database const { id } = req.params; const publisher = await this.publisherService.getPublisher(id).catch(err => { logger.logError(err, logCategory); @@ -28,15 +28,11 @@ export default class PublisherController extends Controller { publisher: { dataRequestModalContent: {}, allowsMessaging: false }, }); } - // 2. Return publisher + return res.status(200).json({ success: true, publisher }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred fetching the custodian details', - }); + throw new HttpExceptions(`An error occurred fetching the custodian details`, 500); } } @@ -47,43 +43,35 @@ export default class PublisherController extends Controller { async getPublisherDatasets(req, res) { try { - // 1. Get the datasets for the publisher from the database const { id } = req.params; let datasets = await this.publisherService.getPublisherDatasets(id).catch(err => { logger.logError(err, logCategory); }); - // 2. Return publisher datasets + return res.status(200).json({ success: true, datasets }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred searching for custodian datasets', - }); + throw new HttpExceptions(`An error occurred searching for custodian datasets`, 500); } } async getPublisherDataAccessRequests(req, res) { try { - // 1. Deconstruct the request const { _id: requestingUserId } = req.user; const { id } = req.params; - // 2. Lookup publisher team const options = { lean: true, populate: [{ path: 'team' }, { path: 'members' }] }; const publisher = await this.publisherService.getPublisher(id, options).catch(err => { logger.logError(err, logCategory); }); if (!publisher) { - return res.status(404).json({ success: false }); + throw new HttpExceptions(`Not Found`, 404); } - // 3. Check the requesting user is a member of the custodian team - const isAuthenticated = teamController.checkTeamPermissions('', publisher.team, requestingUserId); - if (!isAuthenticated) return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + + teamV3Util.checkUserRolesByTeam([], publisher.team, requestingUserId); //Check if current user is a manager - const isManager = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, publisher.team, requestingUserId); + const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisher.team, requestingUserId); // 4. Find all applications for current team member view const applications = await this.publisherService.getPublisherDataAccessRequests(id, requestingUserId, isManager).catch(err => { @@ -109,21 +97,16 @@ export default class PublisherController extends Controller { .sort((a, b) => b.updatedAt - a.updatedAt); const avgDecisionTime = this.dataRequestService.calculateAvgDecisionTime(applications); - // 6. Return all applications + return res.status(200).json({ success: true, data: modifiedApplications, avgDecisionTime, canViewSubmitted: isManager }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred searching for custodian applications', - }); + throw new HttpExceptions(`An error occurred searching for custodian applications`, 500); } } async getPublisherWorkflows(req, res) { try { - // 1. Get the workflow from the database including the team members to check authorisation const { id } = req.params; let workflows = await this.workflowService.getWorkflowsByPublisher(id).catch(err => { logger.logError(err, logCategory); @@ -144,20 +127,13 @@ export default class PublisherController extends Controller { const { publisher: { team }, } = workflows[0]; - const authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserId); - // 4. If not return unauthorised - if (!authorised) { - return res.status(401).json({ success: false }); - } - // 5. Return payload + + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserId); + return res.status(200).json({ success: true, workflows }); } catch (err) { - // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred searching for custodian workflows', - }); + throw new HttpExceptions(`An error occurred searching for custodian workflows`, 500); } } @@ -167,10 +143,7 @@ export default class PublisherController extends Controller { return res.status(200).json({ success: true }); }); } catch (err) { - return res.status(500).json({ - success: false, - message: 'An error occurred updating data use widget settings', - }); + throw new HttpExceptions(`An error occurred updating data use widget settings`, 500); } } @@ -180,10 +153,7 @@ export default class PublisherController extends Controller { return res.status(200).json({ success: true }); }); } catch (err) { - return res.status(500).json({ - success: false, - message: 'An error occurred updating data request modal content', - }); + throw new HttpExceptions(`An error occurred updating data request modal content`, 500); } } @@ -193,10 +163,7 @@ export default class PublisherController extends Controller { return res.status(200).json({ success: true }); }); } catch (err) { - return res.status(500).json({ - success: false, - message: 'An error occurred updating the question bank settings', - }); + throw new HttpExceptions(`An error occurred updating the question bank settings`, 500); } } } diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index ce87c210..417464b1 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -274,6 +274,30 @@ const checkAllowNewRoles = (userUpdateRoles, allowedRoles) => { return true; }; +const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { + if (has(team, 'members')) { + let { members } = team; + + let userMember = members.find(el => el.memberid.toString() === userId.toString()); + + if (userMember) { + let { roles = [] } = userMember; + + if (!arrayCheckRoles.length) { + return true; + } + + const check = roles.filter(element => arrayCheckRoles.includes(element)).length; + if (check) { + return true; + } + + } + } + + throw new HttpExceptions(`User not authorized to perform this action`,403); +} + export default { checkTeamV3Permissions, checkIfAdmin, @@ -286,4 +310,5 @@ export default { getAllRolesForApproverUser, listOfRolesAllowed, checkAllowNewRoles, + checkUserRolesByTeam, } \ No newline at end of file From b4361b0b197e8594ad8ff916b6e7ffcdca9dc480 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 3 Feb 2023 15:35:24 +0000 Subject: [PATCH 088/229] added test on checkUserRolesByTeam --- .../__mocks__/checkUserRolesByTeam.mock.js | 156 ++++++++++++++++++ .../__tests__/checkUserRolesByTeam.test.js | 38 +++++ 2 files changed, 194 insertions(+) create mode 100644 src/resources/utilities/__mocks__/checkUserRolesByTeam.mock.js create mode 100644 src/resources/utilities/__tests__/checkUserRolesByTeam.test.js diff --git a/src/resources/utilities/__mocks__/checkUserRolesByTeam.mock.js b/src/resources/utilities/__mocks__/checkUserRolesByTeam.mock.js new file mode 100644 index 00000000..9bb27341 --- /dev/null +++ b/src/resources/utilities/__mocks__/checkUserRolesByTeam.mock.js @@ -0,0 +1,156 @@ +const mockArrayCheckRolesEmptyRole = []; +const mockArrayCheckRolesOneRole = ["custodian.dar.manager"]; +const mockArrayCheckRolesMultiRole = ["custodian.dar.manager", "reviewer"]; +const mockArrayCheckRolesManagerRole = ["manager"]; +const mockTeam = { + "_id": "5f3f98068af2ef61552e1d75", + "active": true, + "members": [ + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [ + { + "optIn": true, + "_id": "606ddfb9130e8f3f3d431801", + "notificationType": "dataAccessRequest", + "message": "" + } + ] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "notifications": [], + "memberid": "61825367cefce1bfe5c9ba7c" + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "metadata_editor" + ], + "memberid": "61d489c85a45342ce44b0bc9", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623483baff441ae7fec9fd43", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d945d9fb5b536d1520c618", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "623467f6ce42aab1cfc29021", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "5ec2a116b293e07eb48afe14", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62028ae4d62405c442fd383f", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + } + ], + "type": "publisher", + "notifications": [ + { + "notificationType": "dataAccessRequest", + "optIn": false, + "subscribedEmails": [ + "nita.dan2@gmail.com" + ], + "_id": "62e79fb6892f2f85d9bc2555" + } + ], + "__v": 11, + "updatedAt": "2022-12-09T15:05:33.512Z" +}; +const mockUserId = "61f91d232e175937b960e213"; + +export { + mockArrayCheckRolesEmptyRole, + mockArrayCheckRolesOneRole, + mockArrayCheckRolesMultiRole, + mockArrayCheckRolesManagerRole, + mockTeam, + mockUserId, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js b/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js new file mode 100644 index 00000000..12881b5f --- /dev/null +++ b/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js @@ -0,0 +1,38 @@ +import teamV3Util from '../team.v3.util'; +import { + mockArrayCheckRolesEmptyRole, + mockArrayCheckRolesOneRole, + mockArrayCheckRolesMultiRole, + mockArrayCheckRolesManagerRole, + mockTeam, + mockUserId, +} from '../__mocks__/checkUserRolesByTeam.mock'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; + +describe('checkUserRolesByTeam test', () => { + it('should return true for no role request', () => { + let response = teamV3Util.checkUserRolesByTeam(mockArrayCheckRolesEmptyRole, mockTeam, mockUserId); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return true for one role request', () => { + let response = teamV3Util.checkUserRolesByTeam(mockArrayCheckRolesOneRole, mockTeam, mockUserId); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return true for multi role request', () => { + let response = teamV3Util.checkUserRolesByTeam(mockArrayCheckRolesMultiRole, mockTeam, mockUserId); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return an exception for manager role', () => { + try { + teamV3Util.checkUserRolesByTeam(mockArrayCheckRolesManagerRole, mockTeam, mockUserId); + } catch (error) { + expect(error).toBeInstanceOf(HttpExceptions); + } + }); +}); \ No newline at end of file From 2af134669d81b5102750a766c75fce0dfb143360 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Feb 2023 16:13:05 +0000 Subject: [PATCH 089/229] update DAR Manager [Workflow Permissions] --- src/resources/workflow/workflow.controller.js | 104 +++++------------- 1 file changed, 25 insertions(+), 79 deletions(-) diff --git a/src/resources/workflow/workflow.controller.js b/src/resources/workflow/workflow.controller.js index a48b6b7c..3dff1127 100644 --- a/src/resources/workflow/workflow.controller.js +++ b/src/resources/workflow/workflow.controller.js @@ -4,10 +4,11 @@ import Mongoose from 'mongoose'; import { PublisherModel } from '../publisher/publisher.model'; import { DataRequestModel } from '../datarequest/datarequest.model'; import { WorkflowModel } from './workflow.model'; -import teamController from '../team/team.controller'; +import teamV3Util from '../utilities/team.v3.util'; import helper from '../utilities/helper.util'; import constants from '../utilities/constants.util'; import Controller from '../base/controller'; +import HttpExceptions from '../../exceptions/HttpExceptions'; export default class WorkflowController extends Controller { constructor(workflowService) { @@ -41,15 +42,12 @@ export default class WorkflowController extends Controller { }, ]); if (!workflow) { - return res.status(404).json({ success: false }); + throw new HttpExceptions(`Workflow not found`, 404); } // 2. Check the requesting user is a manager of the custodian team let { _id: userId } = req.user; - let authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, workflow.publisher.team.toObject(), userId); - // 3. If not return unauthorised - if (!authorised) { - return res.status(401).json({ success: false }); - } + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + // 4. Build workflow response let { active, _id, id, workflowName, version, steps, applications = [] } = workflow.toObject(); applications = applications.map(app => { @@ -78,10 +76,7 @@ export default class WorkflowController extends Controller { }); } catch (err) { console.error(err.message); - return res.status(500).json({ - success: false, - message: 'An error occurred searching for the specified workflow', - }); + throw new HttpExceptions(`An error occurred searching for the specified workflow`, 500); } } @@ -91,10 +86,7 @@ export default class WorkflowController extends Controller { // 1. Look at the payload for the publisher passed const { workflowName = '', publisher = '', steps = [] } = req.body; if (_.isEmpty(workflowName.trim()) || _.isEmpty(publisher.trim()) || _.isEmpty(steps)) { - return res.status(400).json({ - success: false, - message: 'You must supply a workflow name, publisher, and at least one step definition to create a workflow', - }); + throw new HttpExceptions(`You must supply a workflow name, publisher, and at least one step definition to create a workflow`, 400); } // 2. Look up publisher and team const publisherObj = await PublisherModel.findOne({ //lgtm [js/sql-injection] @@ -108,18 +100,10 @@ export default class WorkflowController extends Controller { }); if (!publisherObj) { - return res.status(400).json({ - success: false, - message: 'You must supply a valid publisher to create the workflow against', - }); + throw new HttpExceptions(`You must supply a valid publisher to create the workflow against`, 400); } - // 3. Check the requesting user is a manager of the custodian team - let authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, publisherObj.team.toObject(), userId); + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisherObj.team.toObject(), userId); - // 4. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } // 5. Create new workflow model const id = helper.generatedNumericId(); // 6. set workflow obj for saving @@ -133,10 +117,7 @@ export default class WorkflowController extends Controller { // 7. save new workflow to db workflow = await workflow.save().catch(err => { if (err) { - return res.status(400).json({ - success: false, - message: err.message, - }); + throw new HttpExceptions(`ERROR SAVE WORKFLOW: ${err.message}`, 400); } }); // 8. populate the workflow with the needed fields for our new notification and email @@ -159,10 +140,7 @@ export default class WorkflowController extends Controller { }); } catch (err) { console.error(err.message); - return res.status(500).json({ - success: false, - message: 'An error occurred creating the workflow', - }); + throw new HttpExceptions(`An error occurred creating the workflow`, 500); } } @@ -182,24 +160,17 @@ export default class WorkflowController extends Controller { }, }); if (!workflow) { - return res.status(404).json({ success: false }); - } - // 2. Check the requesting user is a manager of the custodian team - let authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, workflow.publisher.team.toObject(), userId); - // 3. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Workflow not Found`, 404); } + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + // 4. Ensure there are no in-review DARs with this workflow const applications = await DataRequestModel.countDocuments({ workflowId, applicationStatus: 'inReview', }); if (applications > 0) { - return res.status(400).json({ - success: false, - message: 'A workflow which is attached to applications currently in review cannot be edited', - }); + throw new HttpExceptions(`A workflow which is attached to applications currently in review cannot be edited`, 400); } // 5. Edit workflow const { workflowName = '', publisher = '', steps = [] } = req.body; @@ -216,10 +187,7 @@ export default class WorkflowController extends Controller { if (isDirty) { workflow = await workflow.save().catch(err => { if (err) { - return res.status(400).json({ - success: false, - message: err.message, - }); + throw new HttpExceptions(`ERROR SAVE WORKFLOW: ${err.message}`, 400); } }); @@ -233,10 +201,7 @@ export default class WorkflowController extends Controller { }, }); if (!publisherObj) { - return res.status(400).json({ - success: false, - message: 'You must supply a valid publisher to create the workflow against', - }); + throw new HttpExceptions(`You must supply a valid publisher to create the workflow against`, 400); } const detailedWorkflow = await WorkflowModel.findById(workflow._id).populate({ path: 'steps.reviewers', @@ -260,10 +225,7 @@ export default class WorkflowController extends Controller { } } catch (err) { console.error(err.message); - return res.status(500).json({ - success: false, - message: 'An error occurred editing the workflow', - }); + throw new HttpExceptions(`An error occurred editing the workflow`, 500); } } @@ -285,24 +247,17 @@ export default class WorkflowController extends Controller { const { workflowName = '', publisher = {}, steps = [] } = workflow; if (!workflow) { - return res.status(404).json({ success: false }); - } - // 2. Check the requesting user is a manager of the custodian team - let authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, workflow.publisher.team.toObject(), userId); - // 3. Refuse access if not authorised - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Workflow not Found`, 404); } + teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + // 4. Ensure there are no in-review DARs with this workflow const applications = await DataRequestModel.countDocuments({ workflowId, applicationStatus: 'inReview', }); if (applications > 0) { - return res.status(400).json({ - success: false, - message: 'A workflow which is attached to applications currently in review cannot be deleted', - }); + throw new HttpExceptions(`A workflow which is attached to applications currently in review cannot be deleted`, 400); } const detailedWorkflow = await WorkflowModel.findById(workflowId).populate({ path: 'steps.reviewers', @@ -313,10 +268,7 @@ export default class WorkflowController extends Controller { WorkflowModel.deleteOne({ _id: workflowId }, function (err) { if (err) { console.error(err.message); - return res.status(400).json({ - success: false, - message: 'An error occurred deleting the workflow', - }); + throw new HttpExceptions(`An error occurred deleting the workflow`, 400); } }); const publisherObj = await PublisherModel.findOne({ @@ -329,10 +281,7 @@ export default class WorkflowController extends Controller { }, }); if (!publisherObj) { - return res.status(400).json({ - success: false, - message: 'You must supply a valid publisher to create the workflow against', - }); + throw new HttpExceptions(`You must supply a valid publisher to create the workflow against`, 400); } let context = { publisherObj: publisherObj.team.toObject(), @@ -346,10 +295,7 @@ export default class WorkflowController extends Controller { }); } catch (err) { console.error(err.message); - return res.status(500).json({ - success: false, - message: 'An error occurred deleting the workflow', - }); + throw new HttpExceptions(`An error occurred deleting the workflow`, 500); } } } From db99e9d0600ddcd93eca44a44ea0a15209217df7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 13:11:20 +0000 Subject: [PATCH 090/229] DAR Manager [Edit DAR Form Permissions] & update team response --- .../questionbank/questionbank.controller.js | 21 ++++------------ .../questionbank/questionbank.route.js | 24 ++++++++----------- .../__mocks__/formatTeamMembers.mock.js | 8 +++++++ src/resources/utilities/team.v3.util.js | 1 + 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index f6caff38..c93b3e42 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -1,5 +1,6 @@ import Controller from '../base/controller'; import { logger } from '../utilities/logger'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const logCategory = 'questionbank'; @@ -22,10 +23,7 @@ export default class QuestionbankController extends Controller { } catch (err) { // Return error response if something goes wrong console.error(err.message); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again`, 500); } } @@ -43,10 +41,7 @@ export default class QuestionbankController extends Controller { } catch (err) { // Return error response if something goes wrong logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again`, 500); } } @@ -59,10 +54,7 @@ export default class QuestionbankController extends Controller { return res.status(200).json({ success: true, result: newRequestSchema }); } catch (err) { logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again`, 500); } } @@ -76,10 +68,7 @@ export default class QuestionbankController extends Controller { return res.status(200).json({ success: true }); } catch (err) { logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'Error removing the schema updates, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again`, 500); } } } diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index 54dd3dd9..acc9d1b6 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -7,6 +7,7 @@ import { datarequestschemaService } from './../datarequest/schema/dependency'; import { logger } from '../utilities/logger'; import { isUserMemberOfTeamById, isUserMemberOfTeamByName } from '../auth/utils'; import constants from '../utilities/constants.util'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const router = express.Router(); const questionbankController = new QuestionbankController(questionbankService); @@ -15,7 +16,9 @@ const logCategory = 'questionbank'; const validateViewRequest = (req, res, next) => { const { publisherId } = req.params; - if (isUndefined(publisherId)) return res.status(400).json({ success: false, message: 'You must provide a valid publisher Id' }); + if (isUndefined(publisherId)) { + throw new HttpExceptions(`You must provide a valid publisher Id`, 400); + } next(); }; @@ -28,10 +31,7 @@ const authorizeViewRequest = (req, res, next) => { const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); if (!authorised && !isAdminUser) { - return res.status(401).json({ - success: false, - message: 'You are not authorised to perform this action', - }); + throw new HttpExceptions(`You are not authorised to perform this action`, 401); } next(); @@ -40,7 +40,9 @@ const authorizeViewRequest = (req, res, next) => { const validatePostRequest = (req, res, next) => { const { schemaId } = req.params; - if (isUndefined(schemaId)) return res.status(400).json({ success: false, message: 'You must provide a valid data request schema Id' }); + if (isUndefined(schemaId)) { + throw new HttpExceptions(`You must provide a valid data request schema Id`, 400); + } next(); }; @@ -52,20 +54,14 @@ const authorizePostRequest = async (req, res, next) => { const dataRequestSchema = await datarequestschemaService.getDatarequestschemaById(schemaId); if (isEmpty(dataRequestSchema)) { - return res.status(404).json({ - success: false, - message: 'The requested data request schema could not be found', - }); + throw new HttpExceptions(`The requested data request schema could not be found`, 404); } const authorised = isUserMemberOfTeamByName(requestingUser, dataRequestSchema.publisher); const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); if (!authorised && !isAdminUser) { - return res.status(401).json({ - success: false, - message: 'You are not authorised to perform this action', - }); + throw new HttpExceptions(`You are not authorised to perform this action`, 401); } req.body.dataRequestSchema = dataRequestSchema; diff --git a/src/resources/utilities/__mocks__/formatTeamMembers.mock.js b/src/resources/utilities/__mocks__/formatTeamMembers.mock.js index 0d0416eb..c1a94c8d 100644 --- a/src/resources/utilities/__mocks__/formatTeamMembers.mock.js +++ b/src/resources/utilities/__mocks__/formatTeamMembers.mock.js @@ -364,6 +364,7 @@ const mockResponse = [ { firstname: 'Varsha', lastname: 'Khodiyar', + id: 3201332329402178, userId: '6167edbd5306ac30d5b1da14', email: 'varsha.khodiyar@hdruk.ac.uk', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -373,6 +374,7 @@ const mockResponse = [ { firstname: 'Louis', lastname: 'Grandjean', + id: 4539054910544198, userId: '61a519eb06447b0e9b6ae3fd', email: 'Louis.grandjean@gosh.nhs.uk', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -382,6 +384,7 @@ const mockResponse = [ { firstname: 'Priti', lastname: 'Pampatwar', + id: 1214329286003002, userId: '61825367cefce1bfe5c9ba7c', email: 'priti.pampatwar@hdruk.ac.uk', roles: ["manager","metadata_editor","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -391,6 +394,7 @@ const mockResponse = [ { firstname: 'Hdr', lastname: 'GatewayAdmin', + id: 5992307808590300, userId: '623483baff441ae7fec9fd43', email: 'hdrgatea@gmail.com', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -400,6 +404,7 @@ const mockResponse = [ { firstname: 'Yemi', lastname: 'Aiyeola', + id: 9829759310154118, userId: '62d945d9fb5b536d1520c618', email: 'yemiayat@gmail.com', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -409,6 +414,7 @@ const mockResponse = [ { firstname: 'Clara', lastname: 'Fennessy', + id: 45222846999444660, userId: '5ec2a116b293e07eb48afe14', email: 'clara.fennessy@hdruk.ac.uk', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -418,6 +424,7 @@ const mockResponse = [ { firstname: 'Vijayalakshmi', lastname: 'Shanmugam', + id: 6644721675822300, userId: '62384f08e5c7245adf17f0fd', email: 'vijisrisan@gmail.com', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], @@ -427,6 +434,7 @@ const mockResponse = [ { firstname: 'Gateway', lastname: 'Custodian', + id: 37447512737860000, userId: '62028ae4d62405c442fd383f', email: 'custodianhdr01@gmail.com', roles: ["manager","custodian.team.admin","custodian.metadata.manager","custodian.dar.manager"], diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 417464b1..bab549fb 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -77,6 +77,7 @@ const formatTeamMembers = team => { return { firstname, lastname, + id, userId: _id.toString(), email, roles, From 10e39afab18310a6b25f43c639c810e37ab8c01b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 16:37:59 +0000 Subject: [PATCH 091/229] http exception team --- src/resources/team/v3/team.controller.js | 300 ++++++++++++----------- src/resources/utilities/team.v3.util.js | 2 +- 2 files changed, 159 insertions(+), 143 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 7aa37430..2c22d965 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -17,178 +17,194 @@ class TeamController extends TeamService { } async getTeamMembers(req, res) { - const teamId = req.params.teamid; - const users = req.user; - const currentUserId = req.user._id; - - const team = await this.getMembersByTeamId(teamId); - - teamV3Util.checkUserAuthorization(currentUserId, '', team, users); - - let members = teamV3Util.formatTeamMembers(team); - - this.sendLogInGoogle({ - action: 'getTeamMembers', - input: { - teamId, - currentUserId, - }, - output: members, - }); - - res.status(200).json({ - members, - }); - } - - async deleteTeamMember(req, res) { - const teamId = req.params.teamid; - const deleteUserId = req.params.memberid; - const userObj = req.user; - const currentUserId = req.user._id; - - const team = await this.getTeamByTeamId(teamId); - - let { members = [], users = [] } = team; - - teamV3Util.checkIfLastManager(members, deleteUserId); - - let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); - if (members.length === updatedMembers.length) { - throw new Error(`The user requested for deletion is not a member of this team.`); - } - - team.members = updatedMembers; try { - team.save(function (err, result) { - if (err) { - throw new HttpExceptions(err.message); - } else { - let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); - - this.sendLogInGoogle({ - action: 'deleteTeamMember', - input: { - teamId, - memberid: deleteUserId, - currentUserId, - }, - output: 'success' - }); - - return res.status(204).json({ - success: true, - }); - } + const teamId = req.params.teamid; + const users = req.user; + const currentUserId = req.user._id; + + const team = await this.getMembersByTeamId(teamId); + + teamV3Util.checkUserAuthorization(currentUserId, '', team, users); + + let members = teamV3Util.formatTeamMembers(team); + + this.sendLogInGoogle({ + action: 'getTeamMembers', + input: { + teamId, + currentUserId, + }, + output: members, + }); + + res.status(200).json({ + members, }); - } catch (e) { - throw new Error(e.message); + } catch (err) { + throw new HttpExceptions(err.message, err.status); } } - async addTeamMember(req, res) { - const teamId = req.params.teamid; - const currentUserId = req.user._id; - const { memberId, roles = [] } = req.body; - - const team = await this.getTeamByTeamId(teamId); - - let { members } = team; - - let checkIfExistMember = members.find(item => item.memberid.toString() === memberId.toString()); - if (checkIfExistMember) { - throw new HttpExceptions(`Member already exists`, 409); - } - - let newMembers = { - roles: roles, - memberid: memberId, - notifications: [] - }; - - team.members = team.members.concat(newMembers); - team.save(async err => { - if (err) { - throw new HttpExceptions(err.message); - } else { - let newUsers = await UserModel.find({ _id: memberId }); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); - const updatedTeam = await this.getMembersByTeamId(teamId); - let users = teamV3Util.formatTeamMembers(updatedTeam); - - this.sendLogInGoogle({ - action: 'addTeamMember', - input: { - teamId, - currentUserId, - body: req.body, - }, - output: users, - }); - - return res.status(201).json({ - success: true, - members: users, - }); - } - }); - } + async deleteTeamMember(req, res) { + try { + const teamId = req.params.teamid; + const deleteUserId = req.params.memberid; + const userObj = req.user; + const currentUserId = req.user._id; - async updateTeamMember(req, res) { - const teamId = req.params.teamid; - const updateUserId = req.params.memberid; - const userObj = req.user; - const userTeams = userObj.teams || []; - const currentUserId = req.user._id; - const { roles = [] } = req.body; + const team = await this.getTeamByTeamId(teamId); - const team = await this.getTeamByTeamId(teamId); + let { members = [], users = [] } = team; - let { members } = team; + teamV3Util.checkIfLastManager(members, deleteUserId); - let checkIfExistMember = members.find(item => item.memberid.toString() === updateUserId.toString()); - if (!checkIfExistMember) { - throw new HttpExceptions(`The member does not exist in the team`, 409); - } - - const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); - teamV3Util.checkAllowNewRoles(roles, approvedRoles); + let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); + if (members.length === updatedMembers.length) { + throw new Error(`The user requested for deletion is not a member of this team.`); + } - team.members.map(member => { - if (member.memberid.toString() === updateUserId.toString()) { - member.roles = roles; + team.members = updatedMembers; + try { + team.save(function (err, result) { + if (err) { + throw new HttpExceptions(err.message); + } else { + let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); + + this.sendLogInGoogle({ + action: 'deleteTeamMember', + input: { + teamId, + memberid: deleteUserId, + currentUserId, + }, + output: 'success' + }); + + return res.status(204).json({ + success: true, + }); + } + }); + } catch (e) { + throw new HttpExceptions(e.message); } - }); + } catch (err) { + throw new HttpExceptions(err.message, err.status); + } + } + async addTeamMember(req, res) { try { + const teamId = req.params.teamid; + const currentUserId = req.user._id; + const { memberId, roles = [] } = req.body; + + const team = await this.getTeamByTeamId(teamId); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === memberId.toString()); + if (checkIfExistMember) { + throw new HttpExceptions(`Member already exists`, 409); + } + + let newMembers = { + roles: roles, + memberid: memberId, + notifications: [] + }; + + team.members = team.members.concat(newMembers); team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let updatedTeam = await this.getMembersByTeamId(teamId); + let newUsers = await UserModel.find({ _id: memberId }); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); + const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); - + this.sendLogInGoogle({ - action: 'updateTeamMember', + action: 'addTeamMember', input: { teamId, - memberid: updateUserId, currentUserId, body: req.body, }, output: users, }); - - return res.json({ + + return res.status(201).json({ success: true, members: users, }); } - }); - } catch (e) { - throw new HttpExceptions(e.message); + }); + } catch (err) { + throw new HttpExceptions(err.message, err.status); + } + } + + async updateTeamMember(req, res) { + try { + const teamId = req.params.teamid; + const updateUserId = req.params.memberid; + const userObj = req.user; + const userTeams = userObj.teams || []; + const currentUserId = req.user._id; + const { roles = [] } = req.body; + + const team = await this.getTeamByTeamId(teamId); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === updateUserId.toString()); + if (!checkIfExistMember) { + throw new HttpExceptions(`The member does not exist in the team`, 409); + } + + const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); + teamV3Util.checkAllowNewRoles(roles, approvedRoles); + + team.members.map(member => { + if (member.memberid.toString() === updateUserId.toString()) { + member.roles = roles; + } + }); + + try { + team.save(async err => { + if (err) { + throw new HttpExceptions(err.message); + } else { + let updatedTeam = await this.getMembersByTeamId(teamId); + let users = teamV3Util.formatTeamMembers(updatedTeam); + + this.sendLogInGoogle({ + action: 'updateTeamMember', + input: { + teamId, + memberid: updateUserId, + currentUserId, + body: req.body, + }, + output: users, + }); + + return res.json({ + success: true, + members: users, + }); + } + }); + } catch (e) { + throw new HttpExceptions(e.message); + } + } catch (err) { + throw new HttpExceptions(err.message, err.status); } } diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index bab549fb..85a112dd 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -206,7 +206,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { authorised = checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); } if (!authorised) { - throw new Error(`Not enough permissions. User is not authorized to perform this action.`); + throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); } return true; From 1ca0b27c9f3107d91f36b41652ab8b1c8a4edbc7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 16:56:26 +0000 Subject: [PATCH 092/229] update --- src/resources/team/v3/team.controller.js | 306 +++++++++++------------ src/resources/utilities/team.v3.util.js | 2 +- 2 files changed, 149 insertions(+), 159 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 2c22d965..ff699a7f 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -17,195 +17,185 @@ class TeamController extends TeamService { } async getTeamMembers(req, res) { - try { - const teamId = req.params.teamid; - const users = req.user; - const currentUserId = req.user._id; - - const team = await this.getMembersByTeamId(teamId); - - teamV3Util.checkUserAuthorization(currentUserId, '', team, users); - - let members = teamV3Util.formatTeamMembers(team); - - this.sendLogInGoogle({ - action: 'getTeamMembers', - input: { - teamId, - currentUserId, - }, - output: members, - }); - - res.status(200).json({ - members, - }); - } catch (err) { - throw new HttpExceptions(err.message, err.status); + const teamId = req.params.teamid; + const users = req.user; + const currentUserId = req.user._id; + + const team = await this.getMembersByTeamId(teamId); + + const isAuthorized = teamV3Util.checkUserAuthorization(currentUserId, '', team, users); + if (!isAuthorized) { + return res.status(403).json({ + success: false, + message: `Not enough permissions. User is not authorized to perform this action.`, + }); } + + let members = teamV3Util.formatTeamMembers(team); + + this.sendLogInGoogle({ + action: 'getTeamMembers', + input: { + teamId, + currentUserId, + }, + output: members, + }); + + res.status(200).json({ + members, + }); } async deleteTeamMember(req, res) { - try { - const teamId = req.params.teamid; - const deleteUserId = req.params.memberid; - const userObj = req.user; - const currentUserId = req.user._id; + const teamId = req.params.teamid; + const deleteUserId = req.params.memberid; + const userObj = req.user; + const currentUserId = req.user._id; - const team = await this.getTeamByTeamId(teamId); + const team = await this.getTeamByTeamId(teamId); - let { members = [], users = [] } = team; + let { members = [], users = [] } = team; - teamV3Util.checkIfLastManager(members, deleteUserId); + teamV3Util.checkIfLastManager(members, deleteUserId); - let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); - if (members.length === updatedMembers.length) { - throw new Error(`The user requested for deletion is not a member of this team.`); - } + let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); + if (members.length === updatedMembers.length) { + throw new Error(`The user requested for deletion is not a member of this team.`); + } - team.members = updatedMembers; - try { - team.save(function (err, result) { - if (err) { - throw new HttpExceptions(err.message); - } else { - let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); - - this.sendLogInGoogle({ - action: 'deleteTeamMember', - input: { - teamId, - memberid: deleteUserId, - currentUserId, - }, - output: 'success' - }); - - return res.status(204).json({ - success: true, - }); - } - }); - } catch (e) { - throw new HttpExceptions(e.message); - } - } catch (err) { - throw new HttpExceptions(err.message, err.status); + team.members = updatedMembers; + try { + team.save(function (err, result) { + if (err) { + throw new HttpExceptions(err.message); + } else { + let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); + + this.sendLogInGoogle({ + action: 'deleteTeamMember', + input: { + teamId, + memberid: deleteUserId, + currentUserId, + }, + output: 'success' + }); + + return res.status(204).json({ + success: true, + }); + } + }); + } catch (e) { + throw new HttpExceptions(e.message); } } async addTeamMember(req, res) { - try { - const teamId = req.params.teamid; - const currentUserId = req.user._id; - const { memberId, roles = [] } = req.body; - - const team = await this.getTeamByTeamId(teamId); - - let { members } = team; - - let checkIfExistMember = members.find(item => item.memberid.toString() === memberId.toString()); - if (checkIfExistMember) { - throw new HttpExceptions(`Member already exists`, 409); + const teamId = req.params.teamid; + const currentUserId = req.user._id; + const { memberId, roles = [] } = req.body; + + const team = await this.getTeamByTeamId(teamId); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === memberId.toString()); + if (checkIfExistMember) { + throw new HttpExceptions(`Member already exists`, 409); + } + + let newMembers = { + roles: roles, + memberid: memberId, + notifications: [] + }; + + team.members = team.members.concat(newMembers); + team.save(async err => { + if (err) { + throw new HttpExceptions(err.message); + } else { + let newUsers = await UserModel.find({ _id: memberId }); + teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); + const updatedTeam = await this.getMembersByTeamId(teamId); + let users = teamV3Util.formatTeamMembers(updatedTeam); + + this.sendLogInGoogle({ + action: 'addTeamMember', + input: { + teamId, + currentUserId, + body: req.body, + }, + output: users, + }); + + return res.status(201).json({ + success: true, + members: users, + }); + } + }); + } + + async updateTeamMember(req, res) { + const teamId = req.params.teamid; + const updateUserId = req.params.memberid; + const userObj = req.user; + const userTeams = userObj.teams || []; + const currentUserId = req.user._id; + const { roles = [] } = req.body; + + const team = await this.getTeamByTeamId(teamId); + + let { members } = team; + + let checkIfExistMember = members.find(item => item.memberid.toString() === updateUserId.toString()); + if (!checkIfExistMember) { + throw new HttpExceptions(`The member does not exist in the team`, 409); + } + + const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); + const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); + teamV3Util.checkAllowNewRoles(roles, approvedRoles); + + team.members.map(member => { + if (member.memberid.toString() === updateUserId.toString()) { + member.roles = roles; } - - let newMembers = { - roles: roles, - memberid: memberId, - notifications: [] - }; - - team.members = team.members.concat(newMembers); + }); + + try { team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let newUsers = await UserModel.find({ _id: memberId }); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); - const updatedTeam = await this.getMembersByTeamId(teamId); + let updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); - + this.sendLogInGoogle({ - action: 'addTeamMember', + action: 'updateTeamMember', input: { teamId, + memberid: updateUserId, currentUserId, body: req.body, }, output: users, }); - - return res.status(201).json({ + + return res.json({ success: true, members: users, }); } - }); - } catch (err) { - throw new HttpExceptions(err.message, err.status); - } - } - - async updateTeamMember(req, res) { - try { - const teamId = req.params.teamid; - const updateUserId = req.params.memberid; - const userObj = req.user; - const userTeams = userObj.teams || []; - const currentUserId = req.user._id; - const { roles = [] } = req.body; - - const team = await this.getTeamByTeamId(teamId); - - let { members } = team; - - let checkIfExistMember = members.find(item => item.memberid.toString() === updateUserId.toString()); - if (!checkIfExistMember) { - throw new HttpExceptions(`The member does not exist in the team`, 409); - } - - const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); - teamV3Util.checkAllowNewRoles(roles, approvedRoles); - - team.members.map(member => { - if (member.memberid.toString() === updateUserId.toString()) { - member.roles = roles; - } - }); - - try { - team.save(async err => { - if (err) { - throw new HttpExceptions(err.message); - } else { - let updatedTeam = await this.getMembersByTeamId(teamId); - let users = teamV3Util.formatTeamMembers(updatedTeam); - - this.sendLogInGoogle({ - action: 'updateTeamMember', - input: { - teamId, - memberid: updateUserId, - currentUserId, - body: req.body, - }, - output: users, - }); - - return res.json({ - success: true, - members: users, - }); - } - }); - } catch (e) { - throw new HttpExceptions(e.message); - } - } catch (err) { - throw new HttpExceptions(err.message, err.status); - } + }); + } catch (e) { + throw new HttpExceptions(e.message); + } } sendLogInGoogle(message) { diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 85a112dd..59eee001 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -206,7 +206,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { authorised = checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); } if (!authorised) { - throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); + return false; } return true; From 72960d0ed7f8d3b1c425769422add067669886af Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 17:04:50 +0000 Subject: [PATCH 093/229] change nodejs version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f265113a..546f167e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12 +FROM node:16 # Create app directory WORKDIR /usr/src/app From 93922de15500a70614b10c9037d6f52d7f8dfd0a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 17:11:42 +0000 Subject: [PATCH 094/229] tests - undo changes --- src/resources/team/v3/team.controller.js | 8 +------- src/resources/utilities/team.v3.util.js | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index ff699a7f..dbb1777e 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -23,13 +23,7 @@ class TeamController extends TeamService { const team = await this.getMembersByTeamId(teamId); - const isAuthorized = teamV3Util.checkUserAuthorization(currentUserId, '', team, users); - if (!isAuthorized) { - return res.status(403).json({ - success: false, - message: `Not enough permissions. User is not authorized to perform this action.`, - }); - } + teamV3Util.checkUserAuthorization(currentUserId, '', team, users); let members = teamV3Util.formatTeamMembers(team); diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 59eee001..85a112dd 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -206,7 +206,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { authorised = checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); } if (!authorised) { - return false; + throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); } return true; From 573b6a63f54937d58319566e03c34b6ce13a202d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 7 Feb 2023 17:26:23 +0000 Subject: [PATCH 095/229] update model for roles --- src/resources/team/team.model.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/resources/team/team.model.js b/src/resources/team/team.model.js index 5549bf57..87d86726 100644 --- a/src/resources/team/team.model.js +++ b/src/resources/team/team.model.js @@ -20,7 +20,9 @@ const TeamSchema = new Schema( 'metadata_editor', 'custodian.team.admin', 'custodian.metadata.manager', - 'custodian.dar.manager' + 'custodian.dar.manager', + 'admin_dataset', + 'admin_data_use' ], required: true }, From 5a255131f5231ef4762fe4daa9106eccf1399d8b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 8 Feb 2023 15:23:32 +0000 Subject: [PATCH 096/229] added permissions in routes and extra-checking --- src/middlewares/checkAccessTeamMiddleware.js | 2 +- src/resources/team/team.model.js | 3 +- src/resources/team/v3/team.controller.js | 13 +++-- src/resources/team/v3/team.route.js | 22 ++++++++- .../__mocks__/checkIfExistAdminRole.mock.js | 49 +++++++++++++++++++ .../__tests__/checkIfExistAdminRole.test.js | 23 +++++++++ src/resources/utilities/team.v3.util.js | 23 ++++++++- 7 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 src/resources/utilities/__mocks__/checkIfExistAdminRole.mock.js create mode 100644 src/resources/utilities/__tests__/checkIfExistAdminRole.test.js diff --git a/src/middlewares/checkAccessTeamMiddleware.js b/src/middlewares/checkAccessTeamMiddleware.js index 4fa12035..b9c95e1a 100644 --- a/src/middlewares/checkAccessTeamMiddleware.js +++ b/src/middlewares/checkAccessTeamMiddleware.js @@ -10,8 +10,8 @@ const checkAccessToTeamMiddleware = (arrayAllowedPermissions) => (req, res, next throw new HttpExceptions('One or more required parameters missing', 400); } - const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); +console.log(`currentUserRoles : ${JSON.stringify(currentUserRoles)}`); teamV3Util.checkingUserAuthorization(arrayAllowedPermissions, currentUserRoles); next(); diff --git a/src/resources/team/team.model.js b/src/resources/team/team.model.js index 87d86726..cb2d611a 100644 --- a/src/resources/team/team.model.js +++ b/src/resources/team/team.model.js @@ -22,7 +22,8 @@ const TeamSchema = new Schema( 'custodian.metadata.manager', 'custodian.dar.manager', 'admin_dataset', - 'admin_data_use' + 'admin_data_use', + 'editor' ], required: true }, diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index dbb1777e..070786c5 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -23,8 +23,6 @@ class TeamController extends TeamService { const team = await this.getMembersByTeamId(teamId); - teamV3Util.checkUserAuthorization(currentUserId, '', team, users); - let members = teamV3Util.formatTeamMembers(team); this.sendLogInGoogle({ @@ -51,13 +49,20 @@ class TeamController extends TeamService { let { members = [], users = [] } = team; - teamV3Util.checkIfLastManager(members, deleteUserId); - let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); if (members.length === updatedMembers.length) { throw new Error(`The user requested for deletion is not a member of this team.`); } + teamV3Util.checkIfExistAdminRole( + updatedMembers, + [ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_MD_MANAGER + ] + ); + team.members = updatedMembers; try { team.save(function (err, result) { diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index ec7a09c5..ae42e331 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -12,6 +12,13 @@ const router = express.Router(); router.get( '/:teamid/members', passport.authenticate('jwt'), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_MD_MANAGER, + 'editor', + constants.roleMemberTeam.CUST_DAR_REVIEWER + ]), (req, res) => TeamController.getTeamMembers(req, res) ); @@ -21,7 +28,9 @@ router.get( router.delete( '/:teamid/members/:memberid', passport.authenticate('jwt'), - checkAccessToTeamMiddleware([constants.roleMemberTeam.CUST_TEAM_ADMIN]), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN + ]), (req, res) => TeamController.deleteTeamMember(req, res), ); @@ -31,7 +40,11 @@ router.delete( router.post( '/:teamid/members', passport.authenticate('jwt'), - checkAccessToTeamMiddleware([constants.roleMemberTeam.CUST_TEAM_ADMIN]), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_MD_MANAGER + ]), (req, res) => TeamController.addTeamMember(req, res), ); @@ -41,6 +54,11 @@ router.post( router.patch( '/:teamid/members/:memberid', passport.authenticate('jwt'), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_MD_MANAGER + ]), (req, res) => TeamController.updateTeamMember(req, res), ); diff --git a/src/resources/utilities/__mocks__/checkIfExistAdminRole.mock.js b/src/resources/utilities/__mocks__/checkIfExistAdminRole.mock.js new file mode 100644 index 00000000..9cb105d6 --- /dev/null +++ b/src/resources/utilities/__mocks__/checkIfExistAdminRole.mock.js @@ -0,0 +1,49 @@ +const mockMembersTrue = [ + { + "roles": [ + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61128e7ef7ff9cee652532b4", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "613f2c40e8592f8d3add7add", + "notifications": [] + }, + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + } +]; +const mockMembersFalse = [ + { + "roles": [ + "editor" + ], + "memberid": "61128e7ef7ff9cee652532b4", + "notifications": [] + } +]; +const mockRolesAuth = ["custodian.team.admin","custodian.dar.manager","custodian.metadata.manager"]; + +export { + mockMembersTrue, + mockMembersFalse, + mockRolesAuth +} diff --git a/src/resources/utilities/__tests__/checkIfExistAdminRole.test.js b/src/resources/utilities/__tests__/checkIfExistAdminRole.test.js new file mode 100644 index 00000000..a2eb3cc0 --- /dev/null +++ b/src/resources/utilities/__tests__/checkIfExistAdminRole.test.js @@ -0,0 +1,23 @@ +import teamV3Util from '../team.v3.util'; +import { + mockMembersTrue, + mockMembersFalse, + mockRolesAuth, +} from '../__mocks__/checkIfExistAdminRole.mock'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; + +describe('checkIfExistAdminRole test', () => { + it('should return true', () => { + let response = teamV3Util.checkIfExistAdminRole(mockMembersTrue, mockRolesAuth); + expect(typeof response).toBe('boolean'); + expect(response).toBe(true); + }); + + it('should return an exception', () => { + try { + teamV3Util.checkIfExistAdminRole(mockMembersFalse, mockRolesAuth); + } catch (error) { + expect(error).toBeInstanceOf(HttpExceptions); + } + }); +}); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 85a112dd..88aca936 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -214,7 +214,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { const allow = arrayCurrentUserRoles.filter(element => arrayRolesAllow.includes(element)).length; - + if (!allow) { throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); } @@ -225,8 +225,26 @@ const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { const checkIfLastManager = (members, deleteUserId) => { let managerCount = members.filter(mem => mem.roles.includes('manager') && mem.memberid.toString() !== deleteUserId).length; if (managerCount === 0) { - throw new Error(`You cannot delete the last manager in the team.`); + throw new HttpExceptions(`You cannot delete the last manager in the team.`); + } +} + +const checkIfExistAdminRole = (members, roles) => { + let checkingMemberRoles; + let checkingMembers = members.map(member => { + checkingMemberRoles = member.roles.filter(role => roles.includes(role)).length; + if (checkingMemberRoles) { + return member.memberid; + } + }); + + const filteredArray = _.compact(checkingMembers).length; + + if (!filteredArray) { + throw new HttpExceptions(`The user requested for deletion is not a member of this team.`); } + + return true; } const getAllRolesForApproverUser = (team, teamId, userId) => { @@ -308,6 +326,7 @@ export default { checkUserAuthorization, checkingUserAuthorization, checkIfLastManager, + checkIfExistAdminRole, getAllRolesForApproverUser, listOfRolesAllowed, checkAllowNewRoles, From 62a9508ae475f25fbaad7548921a01cc1c608b9c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 8 Feb 2023 15:24:58 +0000 Subject: [PATCH 097/229] update middleware --- src/middlewares/checkAccessTeamMiddleware.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/middlewares/checkAccessTeamMiddleware.js b/src/middlewares/checkAccessTeamMiddleware.js index b9c95e1a..8373e4b8 100644 --- a/src/middlewares/checkAccessTeamMiddleware.js +++ b/src/middlewares/checkAccessTeamMiddleware.js @@ -11,7 +11,6 @@ const checkAccessToTeamMiddleware = (arrayAllowedPermissions) => (req, res, next } const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); -console.log(`currentUserRoles : ${JSON.stringify(currentUserRoles)}`); teamV3Util.checkingUserAuthorization(arrayAllowedPermissions, currentUserRoles); next(); From cd992795ce2700a5184a6b65deb29cebb99c0b90 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 9 Feb 2023 12:42:26 +0000 Subject: [PATCH 098/229] update patch perms for dar manager --- src/resources/auth/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index b3e1ff55..13baaa87 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -205,7 +205,7 @@ const loginAndSignToken = (req, res, next) => { const userIsTeamManager = () => async (req, res, next) => { const { user, params } = req; const members = await TeamModel.findOne({ _id: params.id }, { _id: 0, members: { $elemMatch: { memberid: user._id } } }).lean(); - if ((!isEmpty(members) && members.members[0].roles.includes(constants.roleTypes.MANAGER)) || user.role === 'Admin') return next(); + if ((!isEmpty(members) && members.members[0].roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) || user.role === 'Admin') return next(); return res.status(401).json({ status: 'error', From a2e7a93293018018fd9df337f0a542dcaf212d3d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 9 Feb 2023 15:26:53 +0000 Subject: [PATCH 099/229] update get teams perms --- src/resources/team/v3/team.route.js | 8 +------- src/resources/utilities/team.v3.util.js | 4 ++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index ae42e331..f052cc90 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -12,13 +12,7 @@ const router = express.Router(); router.get( '/:teamid/members', passport.authenticate('jwt'), - checkAccessToTeamMiddleware([ - constants.roleMemberTeam.CUST_TEAM_ADMIN, - constants.roleMemberTeam.CUST_DAR_MANAGER, - constants.roleMemberTeam.CUST_MD_MANAGER, - 'editor', - constants.roleMemberTeam.CUST_DAR_REVIEWER - ]), + checkAccessToTeamMiddleware([]), (req, res) => TeamController.getTeamMembers(req, res) ); diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 88aca936..91a0cd40 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -213,6 +213,10 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { }; const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { + let arrayRolesAllowLength = arrayRolesAllow.length; + if (!arrayRolesAllowLength) { + return true; + } const allow = arrayCurrentUserRoles.filter(element => arrayRolesAllow.includes(element)).length; if (!allow) { From 3030c50ca2d64aa45f3e85521783bc808ebd6c73 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 10 Feb 2023 09:29:34 +0000 Subject: [PATCH 100/229] Metadata Manager [Dataset Onboarding] --- src/utils/datasetonboarding.util.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 01d676ed..ad90677b 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -50,7 +50,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(teams.filter(team => team.type === constants.teamTypes.ADMIN))) { isMetadataAdmin = teams .filter(team => team.type === constants.teamTypes.ADMIN) - .find(team => team.roles.includes(constants.roleTypes.ADMIN_DATASET)); + .find(team => team.roles.includes(constants.roleMemberTeam.CUST_MD_MANAGER)); } if (!isEmpty(isMetadataAdmin)) { @@ -70,8 +70,8 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(publisherTeam)) { if (publisherTeam.roles.find(role => role.includes(constants.roleTypes.METADATA_EDITOR))) { return { authorised: true, userType: constants.roleTypes.METADATA_EDITOR }; - } else if (publisherTeam.roles.find(role => role.includes(constants.roleTypes.MANAGER))) { - return { authorised: true, userType: constants.roleTypes.MANAGER }; + } else if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { + return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_MANAGER }; } } From 386c6c5be6871875487cff574cce1382fc85c608 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:13:08 +0000 Subject: [PATCH 101/229] GAT-1887: Creating uat pipelline --- Chart.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 Chart.yaml diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 00000000..5eb312c6 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1 @@ +v0.0.0 \ No newline at end of file From f7df0b024b1a5f765827cc69a850604485a45389 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:14:41 +0000 Subject: [PATCH 102/229] GAT-1887: Creating uat pipelline --- .github/workflows/uat_deployment.yaml | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/uat_deployment.yaml diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml new file mode 100644 index 00000000..676926cb --- /dev/null +++ b/.github/workflows/uat_deployment.yaml @@ -0,0 +1,66 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'release' + push: + branches: + - 'GAT-1887-UAT' + + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben. + # catchsha: + # uses: HDRUK/gateway-api/.github/workflows/dev_deployment.yaml@dev + deploy: + # if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-uat + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: release + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + # run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" + + # - name: Get SHA + # id: getsha + # run: echo ${{ needs.catchsha.outputs.GITHUB_SHA }} + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:{{ steps.catchsha.outputs.GITHUB_SHA}} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # Functionality not supported by Github Actions one to ccheck back agin in the future + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' \ No newline at end of file From 27e21e37dddc69bd69fef2ee2ffbf98cd5971316 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:20:35 +0000 Subject: [PATCH 103/229] GAT-1887: Add multiple tags --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 88d1d4dc..e7b63392 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -57,8 +57,8 @@ jobs: GAR_NAME: ${{ secrets.GAR_NAME_API }} run: |- - docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} ./ - docker push '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} + docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest ./ + docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} # END - Docker auth and build From ea2f98ec42aa31e541424f901225e3bfe08ce556 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:21:22 +0000 Subject: [PATCH 104/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 95e94cdd..5eb312c6 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 \ No newline at end of file +v0.0.0 \ No newline at end of file From 734c261dd9ae471fa9a1882585df145d8192939a Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:26:40 +0000 Subject: [PATCH 105/229] GAT-1887: Testing --- .github/workflows/dev_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index e7b63392..1813eab1 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -11,7 +11,7 @@ env: jobs: build: - needs: analyze + # needs: analyze # Add 'id-token' with the intended permissions for workload identity federation permissions: contents: write From 771a32d8b5524a599dea6cf3608af34f87478b8d Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:28:02 +0000 Subject: [PATCH 106/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5eb312c6..95e94cdd 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 \ No newline at end of file +v0.0.1 \ No newline at end of file From a5a46176c3378a4bed933179b2bf5da1a99e4b7d Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:33:15 +0000 Subject: [PATCH 107/229] GAT-1887: Fixing issue --- .github/workflows/dev_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 1813eab1..d49364b5 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -58,7 +58,7 @@ jobs: run: |- docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest ./ - docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} + docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }} # END - Docker auth and build From 3641c90f8d3826611f64c61d0591e3b257293f07 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:34:10 +0000 Subject: [PATCH 108/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 95e94cdd..5eb312c6 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 \ No newline at end of file +v0.0.0 \ No newline at end of file From f42a80705b3bfbf0775038ca65ff50f036f97b3f Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:36:46 +0000 Subject: [PATCH 109/229] GAT-1887: Removing depriciated set-output --- .github/workflows/dev_deployment.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index d49364b5..7dca80b5 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -27,12 +27,13 @@ jobs: - name: Read VERSION file id: getversion - run: echo "::set-output name=version::$(cat Chart.yaml)" - - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: ${{ steps.getversion.outputs.version }} - prerelease: false + run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" + # Used for production + # - uses: "marvinpinto/action-automatic-releases@latest" + # with: + # repo_token: "${{ secrets.GITHUB_TOKEN }}" + # automatic_release_tag: ${{ steps.getversion.outputs.version }} + # prerelease: false - name: Google Auth id: auth @@ -120,7 +121,7 @@ jobs: - name: Read VERSION file id: getversion - run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" - name: Deploy to Cloud Run uses: actions-hub/gcloud@master From 624befb8859b66199473d97b7046881d66a95167 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:45:49 +0000 Subject: [PATCH 110/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5eb312c6..95e94cdd 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 \ No newline at end of file +v0.0.1 \ No newline at end of file From 324e208e8c7285b5fae53855436285de0a36fc24 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:58:55 +0000 Subject: [PATCH 111/229] GAT-1887: Testing if github_output broke the build --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 7dca80b5..a8e8a74e 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -27,7 +27,7 @@ jobs: - name: Read VERSION file id: getversion - run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" + run: echo "::set-output name=version::$(cat Chart.yaml)" # Used for production # - uses: "marvinpinto/action-automatic-releases@latest" # with: @@ -121,7 +121,7 @@ jobs: - name: Read VERSION file id: getversion - run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" + run: echo "::set-output name=version::$(cat Chart.yaml)" - name: Deploy to Cloud Run uses: actions-hub/gcloud@master From 7545be374254f02c6e85aacb88697ce9c838d978 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 15:59:25 +0000 Subject: [PATCH 112/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 95e94cdd..5eb312c6 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 \ No newline at end of file +v0.0.0 \ No newline at end of file From ca4794b5b70ddc9abc3fbdc80d8023ca10d2fbcd Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:09:25 +0000 Subject: [PATCH 113/229] GAT-1887: Removing depriciated set-output --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index a8e8a74e..3e3242a9 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -27,7 +27,7 @@ jobs: - name: Read VERSION file id: getversion - run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT # Used for production # - uses: "marvinpinto/action-automatic-releases@latest" # with: @@ -121,7 +121,7 @@ jobs: - name: Read VERSION file id: getversion - run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - name: Deploy to Cloud Run uses: actions-hub/gcloud@master From 21cb6d81bb5cd00fa208e20d60048903715899f6 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:09:58 +0000 Subject: [PATCH 114/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5eb312c6..95e94cdd 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 \ No newline at end of file +v0.0.1 \ No newline at end of file From 47e81acc85b9fef70df952cb1c317dd818eb7fce Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:24:30 +0000 Subject: [PATCH 115/229] GAT-1887: Removing depriciated set-output --- .github/workflows/uat_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml index 676926cb..82def7e6 100644 --- a/.github/workflows/uat_deployment.yaml +++ b/.github/workflows/uat_deployment.yaml @@ -43,7 +43,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure - name: Read VERSION file id: getversion # run: echo "::set-output name=version::$(cat Chart.yaml)" - run: echo "version=$(cat Chart.yaml) >> $GITHUB_OUTPUT" + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT # - name: Get SHA # id: getsha From 14be225295e6bd8d0ddb1f0766bbba6b46afb573 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:27:45 +0000 Subject: [PATCH 116/229] GAT-1887: Testing --- .github/workflows/uat_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml index 82def7e6..5c62b134 100644 --- a/.github/workflows/uat_deployment.yaml +++ b/.github/workflows/uat_deployment.yaml @@ -30,7 +30,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure - name: Checkout uses: actions/checkout@v3 with: - ref: release + ref: GAT-1887-UAT - name: Google Auth id: auth From 0dda88ac0ccc31eb9d9f18d1f67110b3ded92be1 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:31:25 +0000 Subject: [PATCH 117/229] GAT-1887: Clean up --- .github/workflows/uat_deployment.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml index 5c62b134..c4a1b1cb 100644 --- a/.github/workflows/uat_deployment.yaml +++ b/.github/workflows/uat_deployment.yaml @@ -6,9 +6,6 @@ on: - closed branches: - 'release' - push: - branches: - - 'GAT-1887-UAT' env: @@ -30,7 +27,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure - name: Checkout uses: actions/checkout@v3 with: - ref: GAT-1887-UAT + ref: release - name: Google Auth id: auth From dd9f09796a6d28547809311e269adaed0e4d93f0 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:36:03 +0000 Subject: [PATCH 118/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index ae39fab3..95e94cdd 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 +v0.0.1 \ No newline at end of file From 80d5000ffe7c2b05032ea17a3cafaabf98e21a4a Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Fri, 10 Feb 2023 16:39:29 +0000 Subject: [PATCH 119/229] GAT-1887: Testing --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 95e94cdd..5eb312c6 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 \ No newline at end of file +v0.0.0 \ No newline at end of file From 94a80afd2740a12f7e3185cd4199dae875be46bf Mon Sep 17 00:00:00 2001 From: kymmeh Date: Mon, 13 Feb 2023 10:13:18 +0000 Subject: [PATCH 120/229] Reversing pipeline change --- src/resources/auth/auth.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/auth/auth.route.js b/src/resources/auth/auth.route.js index 687959c4..fdc342f3 100644 --- a/src/resources/auth/auth.route.js +++ b/src/resources/auth/auth.route.js @@ -25,7 +25,7 @@ router.get('/status', function (req, res, next) { if (err || !user) { return res.json({ success: true, - data: [{ role: 'Reader', id: null, name: null, loggedIn: false, tempProp: true }], + data: [{ role: 'Reader', id: null, name: null, loggedIn: false }], }); } else { // 1. Reformat teams array for frontend From 5b64556017c427b28e4606e50fcc7648d3cd4c51 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 13 Feb 2023 11:41:18 +0000 Subject: [PATCH 121/229] update delete endpoint teams --- src/resources/team/v3/team.controller.js | 47 ++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 070786c5..073d6347 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -62,6 +62,16 @@ class TeamController extends TeamService { constants.roleMemberTeam.CUST_MD_MANAGER ] ); + + this.sendLogInGoogle({ + action: 'deleteTeamMember', + input: { + teamId, + memberid: deleteUserId, + currentUserId, + }, + output: 'success' + }); team.members = updatedMembers; try { @@ -71,22 +81,13 @@ class TeamController extends TeamService { } else { let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); - - this.sendLogInGoogle({ - action: 'deleteTeamMember', - input: { - teamId, - memberid: deleteUserId, - currentUserId, - }, - output: 'success' - }); - + return res.status(204).json({ success: true, - }); + }); } - }); + }); + } catch (e) { throw new HttpExceptions(e.message); } @@ -112,6 +113,16 @@ class TeamController extends TeamService { notifications: [] }; + this.sendLogInGoogle({ + action: 'addTeamMember', + input: { + teamId, + currentUserId, + body: req.body, + }, + output: users, + }); + team.members = team.members.concat(newMembers); team.save(async err => { if (err) { @@ -122,16 +133,6 @@ class TeamController extends TeamService { const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); - this.sendLogInGoogle({ - action: 'addTeamMember', - input: { - teamId, - currentUserId, - body: req.body, - }, - output: users, - }); - return res.status(201).json({ success: true, members: users, From 6740e94017e8ecddf23e305879bd1a3a5a747d34 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Mon, 13 Feb 2023 16:21:52 +0000 Subject: [PATCH 122/229] GAT-1887: Adding preprod pipeline --- .github/workflows/preprod_deployment.yaml | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/preprod_deployment.yaml diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml new file mode 100644 index 00000000..b5e08ed1 --- /dev/null +++ b/.github/workflows/preprod_deployment.yaml @@ -0,0 +1,55 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'preprod' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-preprod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: preprod + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps \ No newline at end of file From f1bafaaf2bfc56673303b3919f763f63bf3f806c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 14 Feb 2023 11:29:09 +0000 Subject: [PATCH 123/229] update check user perms --- src/middlewares/checkAccessTeamMiddleware.js | 3 +- src/resources/team/v3/team.controller.js | 28 +++++++++++++--- src/resources/team/v3/team.service.js | 34 ++++++++++++++++++++ src/resources/utilities/team.v3.util.js | 16 +++++++-- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/middlewares/checkAccessTeamMiddleware.js b/src/middlewares/checkAccessTeamMiddleware.js index 8373e4b8..fa56d1bb 100644 --- a/src/middlewares/checkAccessTeamMiddleware.js +++ b/src/middlewares/checkAccessTeamMiddleware.js @@ -10,8 +10,7 @@ const checkAccessToTeamMiddleware = (arrayAllowedPermissions) => (req, res, next throw new HttpExceptions('One or more required parameters missing', 400); } - const currentUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - teamV3Util.checkingUserAuthorization(arrayAllowedPermissions, currentUserRoles); + req.allowPerms = arrayAllowedPermissions; next(); } diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 073d6347..422b5e8c 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -18,8 +18,10 @@ class TeamController extends TeamService { async getTeamMembers(req, res) { const teamId = req.params.teamid; - const users = req.user; const currentUserId = req.user._id; + const allowPerms = req.allowPerms || []; + + await this.checkUserAuth(teamId, currentUserId, allowPerms); const team = await this.getMembersByTeamId(teamId); @@ -44,6 +46,9 @@ class TeamController extends TeamService { const deleteUserId = req.params.memberid; const userObj = req.user; const currentUserId = req.user._id; + const allowPerms = req.allowPerms || []; + + await this.checkUserAuth(teamId, currentUserId, allowPerms); const team = await this.getTeamByTeamId(teamId); @@ -97,6 +102,9 @@ class TeamController extends TeamService { const teamId = req.params.teamid; const currentUserId = req.user._id; const { memberId, roles = [] } = req.body; + const allowPerms = req.allowPerms || []; + + await this.checkUserAuth(teamId, currentUserId, allowPerms); const team = await this.getTeamByTeamId(teamId); @@ -144,10 +152,11 @@ class TeamController extends TeamService { async updateTeamMember(req, res) { const teamId = req.params.teamid; const updateUserId = req.params.memberid; - const userObj = req.user; - const userTeams = userObj.teams || []; const currentUserId = req.user._id; const { roles = [] } = req.body; + const allowPerms = req.allowPerms || []; + + const currUserRoles = await this.checkUserAuth(teamId, currentUserId, allowPerms); const team = await this.getTeamByTeamId(teamId); @@ -158,8 +167,7 @@ class TeamController extends TeamService { throw new HttpExceptions(`The member does not exist in the team`, 409); } - const approverUserRoles = teamV3Util.getAllRolesForApproverUser(userTeams, teamId, currentUserId); - const approvedRoles = teamV3Util.listOfRolesAllowed(approverUserRoles, constants.rolesAcceptedByRoles); + const approvedRoles = teamV3Util.listOfRolesAllowed(currUserRoles, constants.rolesAcceptedByRoles); teamV3Util.checkAllowNewRoles(roles, approvedRoles); team.members.map(member => { @@ -198,6 +206,16 @@ class TeamController extends TeamService { } } + async checkUserAuth(teamId, userId, allowPerms) { + const currUserRolesFromTeamPublisher = await this.getPermsByUserIdFromTeamPublisher(teamId, userId); + const currUserRolesFromTeamAdmin = await this.getPermsByUserIdFromTeamAdmin(userId); + const currUserRolesExists = [...currUserRolesFromTeamPublisher, ...currUserRolesFromTeamAdmin]; + const currUserRolesUnique = [...new Set(currUserRolesExists)]; + teamV3Util.checkingUserAuthorization(allowPerms, currUserRolesUnique); + + return currUserRolesUnique; + } + sendLogInGoogle(message) { const loggingEnabled = parseInt(process.env.LOGGING_LOG_ENABLED) || 0; if (loggingEnabled) { diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js index c7207090..c223a64b 100644 --- a/src/resources/team/v3/team.service.js +++ b/src/resources/team/v3/team.service.js @@ -24,6 +24,40 @@ export default class TeamService { } } + async getPermsByUserIdFromTeamPublisher(teamId, userId) { + try { + const team = await TeamModel.findOne({ _id: teamId }); + + if (!team) { + return []; + } + + let userRoles = team.members.find(member => member.memberid.toString() === userId.toString()); + + return userRoles ? userRoles.roles : []; + } catch (e) { + process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); + throw new Error(e.message); + } + } + + async getPermsByUserIdFromTeamAdmin(userId) { + try { + const team = await TeamModel.findOne({ type: 'admin' }); + + if (!team) { + return []; + } + + let userRoles = team.members.find(member => member.memberid.toString() === userId.toString()); + + return userRoles ? userRoles.roles : []; + } catch (e) { + process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); + throw new Error(e.message); + } + } + async getTeamByTeamId(teamId) { try { const team = await TeamModel diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 91a0cd40..a5f8958a 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -4,6 +4,7 @@ import constants from './constants.util'; import emailGenerator from './emailGenerator.util'; import notificationBuilder from './notificationBuilder'; import HttpExceptions from '../../exceptions/HttpExceptions'; +import { TeamModel } from '../team/team.model'; /** * Check a users permission levels for a team @@ -206,7 +207,7 @@ const checkUserAuthorization = (currUserId, permission, team, users) => { authorised = checkIfAdmin(users, [constants.roleTypes.ADMIN_DATASET]); } if (!authorised) { - throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); + throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`, 403); } return true; @@ -220,7 +221,7 @@ const checkingUserAuthorization = (arrayRolesAllow, arrayCurrentUserRoles) => { const allow = arrayCurrentUserRoles.filter(element => arrayRolesAllow.includes(element)).length; if (!allow) { - throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`); + throw new HttpExceptions(`Not enough permissions. User is not authorized to perform this action.`, 403); } return true; @@ -321,6 +322,16 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { throw new HttpExceptions(`User not authorized to perform this action`,403); } +const getUserByUserIdFromTeamId = async (teamId, userId) => { + const team = await TeamModel.findOne({ _id: teamId }).populate({ + path: 'users', + populate: { + path: 'additionalInfo', + select: 'organisation bio showOrganisation showBio news', + }, + }); +}; + export default { checkTeamV3Permissions, checkIfAdmin, @@ -335,4 +346,5 @@ export default { listOfRolesAllowed, checkAllowNewRoles, checkUserRolesByTeam, + getUserByUserIdFromTeamId, } \ No newline at end of file From e865d95804d8172c772a10f9e3d09a62ac65bf7f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 14 Feb 2023 11:30:28 +0000 Subject: [PATCH 124/229] remove method --- src/resources/utilities/team.v3.util.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index a5f8958a..5907c85c 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -322,16 +322,6 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { throw new HttpExceptions(`User not authorized to perform this action`,403); } -const getUserByUserIdFromTeamId = async (teamId, userId) => { - const team = await TeamModel.findOne({ _id: teamId }).populate({ - path: 'users', - populate: { - path: 'additionalInfo', - select: 'organisation bio showOrganisation showBio news', - }, - }); -}; - export default { checkTeamV3Permissions, checkIfAdmin, @@ -346,5 +336,4 @@ export default { listOfRolesAllowed, checkAllowNewRoles, checkUserRolesByTeam, - getUserByUserIdFromTeamId, } \ No newline at end of file From 95ded0201b3d0185dc787c70578f518dd326d33f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 14 Feb 2023 11:31:35 +0000 Subject: [PATCH 125/229] upgrade --- src/resources/team/v3/team.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js index c223a64b..971e2386 100644 --- a/src/resources/team/v3/team.service.js +++ b/src/resources/team/v3/team.service.js @@ -26,7 +26,7 @@ export default class TeamService { async getPermsByUserIdFromTeamPublisher(teamId, userId) { try { - const team = await TeamModel.findOne({ _id: teamId }); + const team = await TeamModel.findOne({ _id: teamId, type: 'publisher' }); if (!team) { return []; From 1ec8942e131c2853a5bd1849248933dca4570339 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 14 Feb 2023 14:36:25 +0000 Subject: [PATCH 126/229] GAT-1887: Adding only dev branch trigger --- .github/workflows/dev_deployment.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 3e3242a9..05a265a6 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -2,8 +2,10 @@ name: Build and Deploy to Cloud Run on: push: + branches: + - 'dev' paths: - - 'Chart.yaml' + - 'Chart.yaml' env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. From 3b3909077d3b6805c1c65f67d412c09c62e580e3 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 14 Feb 2023 14:36:56 +0000 Subject: [PATCH 127/229] GAT-1887: Adding only dev branch trigger --- .github/workflows/dev_deployment.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 05a265a6..849c12b3 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -30,12 +30,6 @@ jobs: - name: Read VERSION file id: getversion run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - # Used for production - # - uses: "marvinpinto/action-automatic-releases@latest" - # with: - # repo_token: "${{ secrets.GITHUB_TOKEN }}" - # automatic_release_tag: ${{ steps.getversion.outputs.version }} - # prerelease: false - name: Google Auth id: auth From e3fc15406c61b2877c4fc0600a8d822fe2bd5a44 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 14 Feb 2023 16:08:32 +0000 Subject: [PATCH 128/229] GAT-1887: Added Production pipeline --- .github/workflows/prod_deployment.yaml | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/prod_deployment.yaml diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml new file mode 100644 index 00000000..686e4e07 --- /dev/null +++ b/.github/workflows/prod_deployment.yaml @@ -0,0 +1,55 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'master' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-prod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: master + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps \ No newline at end of file From 7d158aae92fe365d51aeba21d688c8d4e47232e3 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 14 Feb 2023 16:13:22 +0000 Subject: [PATCH 129/229] Revert "GAT-1887: Testing New Release from preprod to prod gateway-api" --- .github/workflows/dev_deployment.yaml | 138 ---------------------- .github/workflows/preprod_deployment.yaml | 55 --------- .github/workflows/uat_deployment.yaml | 63 ---------- Chart.yaml | 1 - src/config/server.js | 2 - src/resources/auth/auth.route.js | 2 +- src/resources/auth/utils.js | 2 +- src/resources/dataset/dataset.entity.js | 2 +- src/resources/metadata/metadata.route.js | 22 ---- src/utils/datasetonboarding.util.js | 2 +- 10 files changed, 4 insertions(+), 285 deletions(-) delete mode 100644 .github/workflows/dev_deployment.yaml delete mode 100644 .github/workflows/preprod_deployment.yaml delete mode 100644 .github/workflows/uat_deployment.yaml delete mode 100644 Chart.yaml delete mode 100644 src/resources/metadata/metadata.route.js diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml deleted file mode 100644 index 3e3242a9..00000000 --- a/.github/workflows/dev_deployment.yaml +++ /dev/null @@ -1,138 +0,0 @@ -name: Build and Deploy to Cloud Run - -on: - push: - paths: - - 'Chart.yaml' - -env: - PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. - GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location - -jobs: - build: - # needs: analyze - # Add 'id-token' with the intended permissions for workload identity federation - permissions: - contents: write - id-token: write - - runs-on: ubuntu-latest - environment: legacy-dev - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: dev - - - name: Read VERSION file - id: getversion - run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - # Used for production - # - uses: "marvinpinto/action-automatic-releases@latest" - # with: - # repo_token: "${{ secrets.GITHUB_TOKEN }}" - # automatic_release_tag: ${{ steps.getversion.outputs.version }} - # prerelease: false - - - name: Google Auth - id: auth - uses: 'google-github-actions/auth@v0' - with: - token_format: 'access_token' - workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' - service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' - - - name: Login to GAR - uses: docker/login-action@v2 - with: - registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.PROJECT_ID }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} - - - name: Build and Push Container - shell: bash - env: - GAR_LOCATION: ${{ secrets.GAR_LOCATION }} - PROJECT_ID: ${{ secrets.PROJECT_ID }} - GAR_NAME: ${{ secrets.GAR_NAME_API }} - - run: |- - docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest ./ - docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }} - # END - Docker auth and build - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben - deploy: - needs: build - permissions: - contents: write - id-token: write - - runs-on: ubuntu-latest - environment: legacy-dev - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: dev - - - name: Google Auth - id: auth - uses: 'google-github-actions/auth@v0' - with: - token_format: 'access_token' - workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' - service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' - - - name: Read VERSION file - id: getversion - run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - - - name: Deploy to Cloud Run - uses: actions-hub/gcloud@master - id: deploy - env: - PROJECT_ID: ${{ secrets.PROJECT_ID }} - GAR_LOCATION: ${{ secrets.GAR_LOCATION }} - GAR_NAME: ${{ secrets.GAR_NAME_API }} - SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' - SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' - - with: - args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # If required, use the Cloud Run url output in later steps \ No newline at end of file diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml deleted file mode 100644 index b5e08ed1..00000000 --- a/.github/workflows/preprod_deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: Deploy to Cloud Run - -on: - pull_request: - types: - - closed - branches: - - 'preprod' - -env: - PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. - GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location - -jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben - deploy: - if: github.event.pull_request.merged == true - permissions: - contents: write - id-token: write - - runs-on: ubuntu-latest - environment: legacy-preprod - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: preprod - - - name: Google Auth - id: auth - uses: 'google-github-actions/auth@v0' - with: - token_format: 'access_token' - workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' - service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' - - - name: Read VERSION file - id: getversion - run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - - - name: Deploy to Cloud Run - uses: actions-hub/gcloud@master - id: deploy - env: - PROJECT_ID: ${{ secrets.PROJECT_ID }} - DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} - GAR_LOCATION: ${{ secrets.GAR_LOCATION }} - GAR_NAME: ${{ secrets.GAR_NAME_API }} - SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' - SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' - - with: - # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # If required, use the Cloud Run url output in later steps \ No newline at end of file diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml deleted file mode 100644 index c4a1b1cb..00000000 --- a/.github/workflows/uat_deployment.yaml +++ /dev/null @@ -1,63 +0,0 @@ -name: Deploy to Cloud Run - -on: - pull_request: - types: - - closed - branches: - - 'release' - - -env: - PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. - GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location - -jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben. - # catchsha: - # uses: HDRUK/gateway-api/.github/workflows/dev_deployment.yaml@dev - deploy: - # if: github.event.pull_request.merged == true - permissions: - contents: write - id-token: write - - runs-on: ubuntu-latest - environment: legacy-uat - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: release - - - name: Google Auth - id: auth - uses: 'google-github-actions/auth@v0' - with: - token_format: 'access_token' - workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' - service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' - - - name: Read VERSION file - id: getversion - # run: echo "::set-output name=version::$(cat Chart.yaml)" - run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - - # - name: Get SHA - # id: getsha - # run: echo ${{ needs.catchsha.outputs.GITHUB_SHA }} - - - name: Deploy to Cloud Run - uses: actions-hub/gcloud@master - id: deploy - env: - PROJECT_ID: ${{ secrets.PROJECT_ID }} - DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} - GAR_LOCATION: ${{ secrets.GAR_LOCATION }} - GAR_NAME: ${{ secrets.GAR_NAME_API }} - SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' - SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' - - with: - # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:{{ steps.catchsha.outputs.GITHUB_SHA}} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # Functionality not supported by Github Actions one to ccheck back agin in the future - args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' \ No newline at end of file diff --git a/Chart.yaml b/Chart.yaml deleted file mode 100644 index 5eb312c6..00000000 --- a/Chart.yaml +++ /dev/null @@ -1 +0,0 @@ -v0.0.0 \ No newline at end of file diff --git a/src/config/server.js b/src/config/server.js index 57dcc6aa..60b5c46f 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -236,8 +236,6 @@ app.use('/api/v2/questionbank', require('../resources/questionbank/questionbank. app.use('/api/v2/data-use-registers', require('../resources/dataUseRegister/dataUseRegister.route')); app.use('/api/v1/locations', require('../resources/spatialfilter/SpatialRouter')); -app.use('/api/v1/metadata', require('../resources/metadata/metadata.route')); - initialiseAuthentication(app); // launch our backend into a port diff --git a/src/resources/auth/auth.route.js b/src/resources/auth/auth.route.js index 687959c4..fdc342f3 100644 --- a/src/resources/auth/auth.route.js +++ b/src/resources/auth/auth.route.js @@ -25,7 +25,7 @@ router.get('/status', function (req, res, next) { if (err || !user) { return res.json({ success: true, - data: [{ role: 'Reader', id: null, name: null, loggedIn: false, tempProp: true }], + data: [{ role: 'Reader', id: null, name: null, loggedIn: false }], }); } else { // 1. Reformat teams array for frontend diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 231e6543..380d9243 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -129,7 +129,7 @@ const getTeams = async () => { const catchLoginErrorAndRedirect = (req, res, next) => { if (req.auth.err || !req.auth.user) { - if (req.auth.err === 'loginError' || req.auth.user === undefined) { + if (req.auth.err === 'loginError') { return res.status(200).redirect(process.env.homeURL + '/loginerror'); } diff --git a/src/resources/dataset/dataset.entity.js b/src/resources/dataset/dataset.entity.js index 2e00900d..2edc0815 100644 --- a/src/resources/dataset/dataset.entity.js +++ b/src/resources/dataset/dataset.entity.js @@ -50,7 +50,7 @@ export default class DatasetClass extends Entity { transformedObject.dataset['@schema'] = { type: `Dataset`, version: `2.0.0`, - url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/2.1.0/dataset.schema.json`, + url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/latest/dataset.schema.json`, } // Return v2 object diff --git a/src/resources/metadata/metadata.route.js b/src/resources/metadata/metadata.route.js deleted file mode 100644 index cc9e4596..00000000 --- a/src/resources/metadata/metadata.route.js +++ /dev/null @@ -1,22 +0,0 @@ -import express from 'express'; -import passport from 'passport'; - -import { utils } from '../auth'; -import { ROLES } from '../user/user.roles'; - -import datasetonboardingUtil from '../../utils/datasetonboarding.util'; - -const router = express.Router({ mergeParams: true }); - -router.post('/scoring', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin), async (req, res) => { - const { dataset } = req.body; - - if (!dataset) { - res.json({ success: false, error: 'Dataset object must be supplied and contain all required data', status: 400 }); - } - - const verdict = await datasetonboardingUtil.buildMetadataQuality(dataset, dataset.datasetv2, dataset.pid); - res.json({ success: true, data: verdict, status: 200 }); -}); - -module.exports = router; \ No newline at end of file diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index b151df00..139e79c7 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -758,7 +758,7 @@ const buildMetadataQuality = async (dataset, v2Object, pid) => { let rawdata = fs.readFileSync(__dirname + '/schema.json'); schema = JSON.parse(rawdata); - const ajv = new Ajv({ strict: false, allErrors: false }); + const ajv = new Ajv({ strict: false, allErrors: true }); addFormats(ajv); const validate = ajv.compile(schema); validate(cleanV2Object); From 22de1b89da2a2c8c30808882e313013da87deb02 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 14 Feb 2023 16:19:00 +0000 Subject: [PATCH 130/229] Revert "Revert "GAT-1887: Testing New Release from preprod to prod gateway-api"" --- .github/workflows/dev_deployment.yaml | 138 ++++++++++++++++++++++ .github/workflows/preprod_deployment.yaml | 55 +++++++++ .github/workflows/uat_deployment.yaml | 63 ++++++++++ Chart.yaml | 1 + src/config/server.js | 2 + src/resources/auth/auth.route.js | 2 +- src/resources/auth/utils.js | 2 +- src/resources/dataset/dataset.entity.js | 2 +- src/resources/metadata/metadata.route.js | 22 ++++ src/utils/datasetonboarding.util.js | 2 +- 10 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/dev_deployment.yaml create mode 100644 .github/workflows/preprod_deployment.yaml create mode 100644 .github/workflows/uat_deployment.yaml create mode 100644 Chart.yaml create mode 100644 src/resources/metadata/metadata.route.js diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml new file mode 100644 index 00000000..3e3242a9 --- /dev/null +++ b/.github/workflows/dev_deployment.yaml @@ -0,0 +1,138 @@ +name: Build and Deploy to Cloud Run + +on: + push: + paths: + - 'Chart.yaml' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: + build: + # needs: analyze + # Add 'id-token' with the intended permissions for workload identity federation + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-dev + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: dev + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + # Used for production + # - uses: "marvinpinto/action-automatic-releases@latest" + # with: + # repo_token: "${{ secrets.GITHUB_TOKEN }}" + # automatic_release_tag: ${{ steps.getversion.outputs.version }} + # prerelease: false + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Login to GAR + uses: docker/login-action@v2 + with: + registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.PROJECT_ID }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + - name: Build and Push Container + shell: bash + env: + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + PROJECT_ID: ${{ secrets.PROJECT_ID }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + + run: |- + docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest ./ + docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }} + # END - Docker auth and build + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + needs: build + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-dev + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: dev + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps \ No newline at end of file diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml new file mode 100644 index 00000000..b5e08ed1 --- /dev/null +++ b/.github/workflows/preprod_deployment.yaml @@ -0,0 +1,55 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'preprod' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-preprod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: preprod + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps \ No newline at end of file diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml new file mode 100644 index 00000000..c4a1b1cb --- /dev/null +++ b/.github/workflows/uat_deployment.yaml @@ -0,0 +1,63 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'release' + + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben. + # catchsha: + # uses: HDRUK/gateway-api/.github/workflows/dev_deployment.yaml@dev + deploy: + # if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-uat + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: release + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + # run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + # - name: Get SHA + # id: getsha + # run: echo ${{ needs.catchsha.outputs.GITHUB_SHA }} + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:{{ steps.catchsha.outputs.GITHUB_SHA}} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # Functionality not supported by Github Actions one to ccheck back agin in the future + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' \ No newline at end of file diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 00000000..5eb312c6 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1 @@ +v0.0.0 \ No newline at end of file diff --git a/src/config/server.js b/src/config/server.js index 60b5c46f..57dcc6aa 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -236,6 +236,8 @@ app.use('/api/v2/questionbank', require('../resources/questionbank/questionbank. app.use('/api/v2/data-use-registers', require('../resources/dataUseRegister/dataUseRegister.route')); app.use('/api/v1/locations', require('../resources/spatialfilter/SpatialRouter')); +app.use('/api/v1/metadata', require('../resources/metadata/metadata.route')); + initialiseAuthentication(app); // launch our backend into a port diff --git a/src/resources/auth/auth.route.js b/src/resources/auth/auth.route.js index fdc342f3..687959c4 100644 --- a/src/resources/auth/auth.route.js +++ b/src/resources/auth/auth.route.js @@ -25,7 +25,7 @@ router.get('/status', function (req, res, next) { if (err || !user) { return res.json({ success: true, - data: [{ role: 'Reader', id: null, name: null, loggedIn: false }], + data: [{ role: 'Reader', id: null, name: null, loggedIn: false, tempProp: true }], }); } else { // 1. Reformat teams array for frontend diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 380d9243..231e6543 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -129,7 +129,7 @@ const getTeams = async () => { const catchLoginErrorAndRedirect = (req, res, next) => { if (req.auth.err || !req.auth.user) { - if (req.auth.err === 'loginError') { + if (req.auth.err === 'loginError' || req.auth.user === undefined) { return res.status(200).redirect(process.env.homeURL + '/loginerror'); } diff --git a/src/resources/dataset/dataset.entity.js b/src/resources/dataset/dataset.entity.js index 2edc0815..2e00900d 100644 --- a/src/resources/dataset/dataset.entity.js +++ b/src/resources/dataset/dataset.entity.js @@ -50,7 +50,7 @@ export default class DatasetClass extends Entity { transformedObject.dataset['@schema'] = { type: `Dataset`, version: `2.0.0`, - url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/latest/dataset.schema.json`, + url: `https://raw.githubusercontent.com/HDRUK/schemata/master/schema/dataset/2.1.0/dataset.schema.json`, } // Return v2 object diff --git a/src/resources/metadata/metadata.route.js b/src/resources/metadata/metadata.route.js new file mode 100644 index 00000000..cc9e4596 --- /dev/null +++ b/src/resources/metadata/metadata.route.js @@ -0,0 +1,22 @@ +import express from 'express'; +import passport from 'passport'; + +import { utils } from '../auth'; +import { ROLES } from '../user/user.roles'; + +import datasetonboardingUtil from '../../utils/datasetonboarding.util'; + +const router = express.Router({ mergeParams: true }); + +router.post('/scoring', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin), async (req, res) => { + const { dataset } = req.body; + + if (!dataset) { + res.json({ success: false, error: 'Dataset object must be supplied and contain all required data', status: 400 }); + } + + const verdict = await datasetonboardingUtil.buildMetadataQuality(dataset, dataset.datasetv2, dataset.pid); + res.json({ success: true, data: verdict, status: 200 }); +}); + +module.exports = router; \ No newline at end of file diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 139e79c7..b151df00 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -758,7 +758,7 @@ const buildMetadataQuality = async (dataset, v2Object, pid) => { let rawdata = fs.readFileSync(__dirname + '/schema.json'); schema = JSON.parse(rawdata); - const ajv = new Ajv({ strict: false, allErrors: true }); + const ajv = new Ajv({ strict: false, allErrors: false }); addFormats(ajv); const validate = ajv.compile(schema); validate(cleanV2Object); From f6600eacd8acf90bc8bdad5f6cbd019d5247a91b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Feb 2023 10:40:10 +0000 Subject: [PATCH 131/229] update perms --- src/middlewares/checkAccessTeamMiddleware.js | 1 - src/resources/team/v3/team.controller.js | 234 +++++++++++++++++++ src/resources/team/v3/team.route.js | 36 +++ src/resources/team/v3/team.service.js | 27 ++- src/resources/utilities/team.v3.util.js | 79 +++++++ 5 files changed, 371 insertions(+), 6 deletions(-) diff --git a/src/middlewares/checkAccessTeamMiddleware.js b/src/middlewares/checkAccessTeamMiddleware.js index fa56d1bb..de51e826 100644 --- a/src/middlewares/checkAccessTeamMiddleware.js +++ b/src/middlewares/checkAccessTeamMiddleware.js @@ -4,7 +4,6 @@ import teamV3Util from '../resources/utilities/team.v3.util'; const checkAccessToTeamMiddleware = (arrayAllowedPermissions) => (req, res, next) => { const teamId = req.params.teamid || ''; const currentUserId = req.user._id || ''; - const userTeams = req.user.teams || []; if (!teamId || !currentUserId) { throw new HttpExceptions('One or more required parameters missing', 400); diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 422b5e8c..df866731 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -1,11 +1,15 @@ // import { getDBTeamMembers } from './team.database'; +import _, { isEmpty, has, difference, includes, isNull } from 'lodash'; import TeamService from './team.service'; import teamV3Util from '../../utilities/team.v3.util'; import constants from '../../utilities/constants.util'; import HttpExceptions from '../../../exceptions/HttpExceptions'; import { UserModel } from '../../user/user.model'; +import { TeamModel } from '../team.model'; import { LoggingService } from '../../../services'; +import emailGenerator from '../../utilities/emailGenerator.util'; +import notificationBuilder from '../../utilities/notificationBuilder'; class TeamController extends TeamService { _logger; @@ -206,6 +210,236 @@ class TeamController extends TeamService { } } + async getTeamNotifications(req, res) { + const teamId = req.params.teamid; + const currentUserId = req.user._id; + const allowPerms = req.allowPerms || []; + + try { + await this.checkUserAuth(teamId, currentUserId, allowPerms); + + const team = await this.getTeamByTeamIdSimple(teamId); + if (!team) { + throw new HttpExceptions(e.message, 404); + } + + const { + user: { _id }, + } = req; + + let { members } = team; + let authorised = false; + + if (members) { + authorised = members.some(el => el.memberid.toString() === _id.toString()); + } + + if (!authorised) { + throw new HttpExceptions(`You must provide valid authentication credentials to access this resource.`, 401); + } + + let member = [...members].find(el => el.memberid.toString() === _id.toString()); + + const teamNotifications = teamV3Util.formatTeamNotifications(team); + + let notifications = { + memberNotifications: member.notifications ? member.notifications : [], + teamNotifications, + }; + + return res.status(200).json(notifications); + } catch (err) { + process.stdout.write(err.message); + throw new HttpExceptions(`An error occurred retrieving team notifications : ${err.message}`, 500); + } + } + + async updateNotifications(req, res) { + const teamId = req.params.teamid; + const currentUserId = req.user._id; + const allowPerms = req.allowPerms || []; + try { + await this.checkUserAuth(teamId, currentUserId, allowPerms); + + const team = await this.getTeamByTeamId(teamId); + + const { + user: { _id }, + body: data, + } = req; + + let { members, users, notifications } = team; + let authorised = false; + + if (members) { + authorised = [...members].some(el => el.memberid.toString() === _id.toString()); + } + + if (!authorised) return res.status(401).json({ success: false }); + + let member = [...members].find(el => el.memberid.toString() === _id.toString()); + + let isManager = true; + + let { memberNotifications = [], teamNotifications = [] } = data; + + let missingOptIns = {}; + + if (!isEmpty(memberNotifications) && !isEmpty(teamNotifications)) { + missingOptIns = teamV3Util.findMissingOptIns(memberNotifications, teamNotifications); + } + + if (!isEmpty(missingOptIns)) return res.status(400).json({ success: false, message: missingOptIns }); + + if (isManager) { + const optedOutTeamNotifications = Object.values([...teamNotifications]).filter(notification => !notification.optIn) || []; + if (!isEmpty(optedOutTeamNotifications)) { + optedOutTeamNotifications.forEach(teamNotification => { + let { notificationType } = teamNotification; + members.forEach(member => { + let { notifications = [] } = member; + if (!isEmpty(notifications)) { + let notificationIndex = notifications.findIndex(n => n.notificationType.toUpperCase() === notificationType.toUpperCase()); + if (!notifications[notificationIndex].optIn) { + notifications[notificationIndex].optIn = true; + notifications[notificationIndex].message = constants.teamNotificationMessages[notificationType.toUpperCase()]; + } + } + member.notifications = notifications; + }); + }); + } + + if (!isEmpty(notifications)) { + let manager = [...users].find(user => user._id.toString() === member.memberid.toString()); + + [...notifications].forEach(dbNotification => { + let { notificationType } = dbNotification; + const notificationPayload = + [...teamNotifications].find(n => n.notificationType.toUpperCase() === notificationType.toUpperCase()) || {}; + if (!isEmpty(notificationPayload)) { + let { subscribedEmails: dbSubscribedEmails, optIn: dbOptIn } = dbNotification; + let { subscribedEmails: payLoadSubscribedEmails, optIn: payLoadOptIn } = notificationPayload; + const removedEmails = difference([...dbSubscribedEmails], [...payLoadSubscribedEmails]) || []; + const addedEmails = difference([...payLoadSubscribedEmails], [...dbSubscribedEmails]) || []; + const subscribedMembersByType = teamV3Util.filterMembersByNoticationTypes([...members], [notificationType]); + if (!isEmpty(subscribedMembersByType)) { + const memberIds = [...subscribedMembersByType].map(m => m.memberid.toString()); + const { memberEmails, userIds } = teamV3Util.getMemberDetails([...memberIds], [...users]); + let html = ''; + let options = { + managerName: `${manager.firstname} ${manager.lastname}`, + notificationRemoved: false, + disabled: false, + header: '', + emailAddresses: [], + }; + if (!isEmpty(removedEmails) || (dbOptIn && !payLoadOptIn)) { + options = { + ...options, + notificationRemoved: true, + disabled: !payLoadOptIn ? true : false, + header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + } generic team email address(es)`, + emailAddresses: dbOptIn && !payLoadOptIn ? payLoadSubscribedEmails : removedEmails, + publisherId: team.publisher._id.toString(), + }; + html = emailGenerator.generateTeamNotificationEmail(options); + emailGenerator.sendEmail( + memberEmails, + constants.hdrukEmail, + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + } generic team email address(es)`, + html, + true + ); + + notificationBuilder.triggerNotificationMessage( + [...userIds], + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + } generic team email address(es)`, + 'team', + team.publisher ? team.publisher.name : 'Undefined' + ); + } + + if (!isEmpty(addedEmails) || (!dbOptIn && payLoadOptIn)) { + options = { + ...options, + notificationRemoved: false, + header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + } generic team email address(es)`, + emailAddresses: payLoadSubscribedEmails, + publisherId: team.publisher._id.toString(), + }; + html = emailGenerator.generateTeamNotificationEmail(options); + emailGenerator.sendEmail( + memberEmails, + constants.hdrukEmail, + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + } generic team email address(es)`, + html, + true + ); + + notificationBuilder.triggerNotificationMessage( + [...userIds], + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ + !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + } generic team email address(es)`, + 'team', + team.publisher ? team.publisher.name : 'Undefined' + ); + } + } + } + }); + } + team.notifications = teamNotifications; + } + member.notifications = memberNotifications; + await team.save(); + return res.status(201).json(team); + } catch (err) { + process.stdout.write(err.message); + throw new HttpExceptions(`An error occurred updating notification messages : ${err.message}`, 500); + } + } + + async updateNotificationMessages(req, res) { + const teamId = req.params.teamid; + const currentUserId = req.user._id; + const allowPerms = req.allowPerms || []; + + try { + await this.checkUserAuth(teamId, currentUserId, allowPerms); + + const { + user: { _id }, + } = req; + await TeamModel.update( + { _id: teamId }, + { $set: { 'members.$[m].notifications.$[].message': '' } }, + { arrayFilters: [{ 'm.memberid': _id }], multi: true } + ) + .then(resp => { + return res.status(201).json(); + }) + .catch(err => { + process.stdout.write(err.message); + throw new HttpExceptions(`An error occurred updating notification messages : ${err.message}`, 500); + }); + } catch (err) { + process.stdout.write(err.message); + throw new HttpExceptions(`An error occurred updating notification messages : ${err.message}`, 500); + } + } + async checkUserAuth(teamId, userId, allowPerms) { const currUserRolesFromTeamPublisher = await this.getPermsByUserIdFromTeamPublisher(teamId, userId); const currUserRolesFromTeamAdmin = await this.getPermsByUserIdFromTeamAdmin(userId); diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index f052cc90..57eff283 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -56,4 +56,40 @@ router.patch( (req, res) => TeamController.updateTeamMember(req, res), ); +// @route GET api/v3/teams/:teamid/notifications +// @desc Get team notifications +// @access Private +router.get( + '/:teamid/notifications', + passport.authenticate('jwt'), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN + ]), + (req, res) => TeamController.getTeamNotifications(req, res), +); + +// @route PUT api/v3/teams/:teamid/notifications +// @desc Update notifications +// @access Private +router.put( + '/:teamid/notifications', + passport.authenticate('jwt'), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN + ]), + (req, res) => TeamController.updateNotifications(req, res), +); + +// @route PUT api/v3/teams/:teamid/notifications/messages +// @desc Update notifications +// @access Private +router.put( + '/:teamid/notifications/messages', + passport.authenticate('jwt'), + checkAccessToTeamMiddleware([ + constants.roleMemberTeam.CUST_TEAM_ADMIN + ]), + (req, res) => TeamController.updateNotificationMessages(req, res), +); + module.exports = router; \ No newline at end of file diff --git a/src/resources/team/v3/team.service.js b/src/resources/team/v3/team.service.js index 971e2386..2256043a 100644 --- a/src/resources/team/v3/team.service.js +++ b/src/resources/team/v3/team.service.js @@ -1,3 +1,4 @@ +import HttpExceptions from "../../../exceptions/HttpExceptions"; import { TeamModel } from "../team.model"; export default class TeamService { @@ -20,7 +21,7 @@ export default class TeamService { return team; } catch (e) { process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); - throw new Error(e.message); + throw new HttpExceptions(e.message); } } @@ -37,7 +38,7 @@ export default class TeamService { return userRoles ? userRoles.roles : []; } catch (e) { process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); - throw new Error(e.message); + throw new HttpExceptions(e.message); } } @@ -54,7 +55,7 @@ export default class TeamService { return userRoles ? userRoles.roles : []; } catch (e) { process.stdout.write(`TeamController.getTeamMembers : ${e.message}\n`); - throw new Error(e.message); + throw new HttpExceptions(e.message); } } @@ -71,13 +72,29 @@ export default class TeamService { ]); if (!team) { - throw new Error(`Team not Found`); + throw new HttpExceptions(`Team not Found`); } return team; } catch (e) { process.stdout.write(`TeamController.getTeamByTeamId : ${e.message}\n`); - throw new Error(e.message); + throw new HttpExceptions(e.message); + } + } + + async getTeamByTeamIdSimple(teamId) { + try { + const team = await TeamModel + .findOne({ _id: teamId }); + + if (!team) { + throw new Error(`Team not Found`); + } + + return team; + } catch (e) { + process.stdout.write(`TeamController.getTeamByTeamIdSimple : ${e.message}\n`); + throw new HttpExceptions(e.message); } } } diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 5907c85c..decdad0b 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -322,6 +322,81 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { throw new HttpExceptions(`User not authorized to perform this action`,403); } +const formatTeamNotifications = (team) => { + let { notifications = [] } = team; + if (!isEmpty(notifications)) { + + return [...notifications].reduce((arr, notification) => { + let teamNotificationEmails = []; + let { notificationType = '', optIn = false, subscribedEmails = [] } = notification; + + if (!isEmpty(subscribedEmails)) teamNotificationEmails = [...subscribedEmails].map(email => ({ value: email, error: '' })); + else teamNotificationEmails = [{ value: '', error: '' }]; + + let formattedNotification = { + notificationType, + optIn, + subscribedEmails: teamNotificationEmails, + }; + + arr = [...arr, formattedNotification]; + + return arr; + }, []); + } else { + return []; + } +}; + +const findMissingOptIns = (memberNotifications, teamNotifications) => { + return [...memberNotifications].reduce((neededOptIns, memberNotification) => { + let { notificationType: memberNotificationType, optIn: memberOptIn } = memberNotification; + // find the matching notification type within the teams notification + let teamNotification = + [...teamNotifications].find(teamNotification => teamNotification.notificationType === memberNotificationType) || {}; + // if the team has the same notification type test + if (!isEmpty(teamNotification)) { + let { notificationType, optIn: teamOptIn, subscribedEmails } = teamNotification; + // if both are turned off build and return new error + if ((!teamOptIn && !memberOptIn) || (!memberOptIn && subscribedEmails.length <= 0)) { + neededOptIns = { + ...neededOptIns, + [`${notificationType}`]: `Notifications must be enabled for ${constants.teamNotificationTypesHuman[notificationType]}`, + }; + } + } + return neededOptIns; + }, {}); +}; + +const filterMembersByNoticationTypes = (members, notificationTypes) => { + return filter(members, member => { + return some(member.notifications, notification => { + return includes(notificationTypes, notification.notificationType); + }); + }); +}; + +const getMemberDetails = (memberIds = [], users = []) => { + if (!isEmpty(memberIds) && !isEmpty(users)) { + return [...users].reduce( + (arr, user) => { + let { email, id, _id } = user; + if (memberIds.includes(_id.toString())) { + arr['memberEmails'].push({ email }); + arr['userIds'].push({ id }); + } + return { + memberEmails: arr['memberEmails'], + userIds: arr['userIds'], + }; + }, + { memberEmails: [], userIds: [] } + ); + } + return []; +}; + export default { checkTeamV3Permissions, checkIfAdmin, @@ -336,4 +411,8 @@ export default { listOfRolesAllowed, checkAllowNewRoles, checkUserRolesByTeam, + formatTeamNotifications, + findMissingOptIns, + filterMembersByNoticationTypes, + getMemberDetails, } \ No newline at end of file From 28236704bb0167028acbc9d0869f546c22995b61 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 15 Feb 2023 14:26:03 +0000 Subject: [PATCH 132/229] GAT-1887: Added Automated release and tag creation --- .github/workflows/prod_deployment.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index 686e4e07..5152f649 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -37,7 +37,14 @@ jobs: # Deployment please don't modify anything here as the infrastructure - name: Read VERSION file id: getversion run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT - + + - name: Create the release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: 'GAT Live Release - '${{ steps.getversion.outputs.version }} + prerelease: false + - name: Deploy to Cloud Run uses: actions-hub/gcloud@master id: deploy From 65e3f8531179d38d807b36504d665d8c537d1cbb Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Feb 2023 15:19:03 +0000 Subject: [PATCH 133/229] import HttpExceptions in publisher controller --- src/resources/publisher/publisher.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 33781ddf..7e491e28 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -4,6 +4,7 @@ import teamController from '../team/team.controller'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; import teamV3Util from '../utilities/team.v3.util'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const logCategory = 'Publisher'; From e8f7f2a14d4a1f6cea8feb4342ab69f1baab6216 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Feb 2023 15:43:49 +0000 Subject: [PATCH 134/229] update code permissions publishers datarequestaccess --- src/resources/publisher/publisher.controller.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 7e491e28..2fbea3af 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -69,10 +69,8 @@ export default class PublisherController extends Controller { throw new HttpExceptions(`Not Found`, 404); } - teamV3Util.checkUserRolesByTeam([], publisher.team, requestingUserId); - //Check if current user is a manager - const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisher.team, requestingUserId); + const isManager = teamV3Util.checkUserRolesByTeam([], publisher.team, requestingUserId); // 4. Find all applications for current team member view const applications = await this.publisherService.getPublisherDataAccessRequests(id, requestingUserId, isManager).catch(err => { From f7fa6eb5117af4a901d2816739d0383a23897dfa Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Feb 2023 15:58:59 +0000 Subject: [PATCH 135/229] added to be custodian dar manager --- src/resources/publisher/publisher.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 2fbea3af..90523504 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -70,7 +70,7 @@ export default class PublisherController extends Controller { } //Check if current user is a manager - const isManager = teamV3Util.checkUserRolesByTeam([], publisher.team, requestingUserId); + const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisher.team, requestingUserId); // 4. Find all applications for current team member view const applications = await this.publisherService.getPublisherDataAccessRequests(id, requestingUserId, isManager).catch(err => { From 11482a8a2558d5d6131584203cc87955362a5263 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Feb 2023 16:36:02 +0000 Subject: [PATCH 136/229] tests for formatTeamNotifications --- .../__mocks__/formatTeamNotifications.mock.js | 170 ++++++++++++++++++ .../__tests__/formatTeamNotifications.test.js | 13 ++ 2 files changed, 183 insertions(+) create mode 100644 src/resources/utilities/__mocks__/formatTeamNotifications.mock.js create mode 100644 src/resources/utilities/__tests__/formatTeamNotifications.test.js diff --git a/src/resources/utilities/__mocks__/formatTeamNotifications.mock.js b/src/resources/utilities/__mocks__/formatTeamNotifications.mock.js new file mode 100644 index 00000000..6ca31710 --- /dev/null +++ b/src/resources/utilities/__mocks__/formatTeamNotifications.mock.js @@ -0,0 +1,170 @@ +const mockTeam = { + "active": true, + "_id": "5f7b1a2bce9f65e6ed83e7da", + "members": [ + { + "roles": [ + "manager", + "reviewer", + "metadata_editor", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61825367cefce1bfe5c9ba7c", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62349f5f767db5d3408b4007", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62d691a49901cef16d8da801", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "custodian.dar.manager", + "custodian.team.admin", + "metadata_editor" + ], + "memberid": "632c325de4e074719a8c13de", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "62384f08e5c7245adf17f0fd", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "metadata_editor" + ], + "memberid": "62f502413e9bf5e82256d63b", + "notifications": [] + }, + { + "roles": [ + "manager", + "custodian.team.admin", + "custodian.metadata.manager", + "custodian.dar.manager" + ], + "memberid": "61ddbbcdd05e7f703fc3190d", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "admin_dataset" + ], + "memberid": "63d2bfcbd4663faea8745ae6", + "notifications": [] + }, + { + "roles": [ + "custodian.team.admin", + "custodian.dar.manager" + ], + "memberid": "6308bfd1d2ff69e6c13427e7", + "notifications": [] + }, + { + "roles": [ + "custodian.dar.manager", + "reviewer", + "custodian.team.admin" + ], + "memberid": "63d27a30a5959c9bfc72caa2", + "notifications": [] + }, + { + "roles": [ + "custodian.metadata.manager", + "custodian.dar.manager", + "reviewer" + ], + "memberid": "63d3cb845487686dad9552ea", + "notifications": [] + }, + { + "roles": [ + "manager" + ], + "memberid": "634c518998e119341680d558", + "notifications": [] + }, + { + "roles": [ + "reviewer", + "custodian.dar.manager", + "custodian.team.admin", + "custodian.metadata.manager" + ], + "memberid": "63dd1225a87ca70692fcddcf", + "notifications": [] + }, + { + "roles": [ + "reviewer" + ], + "memberid": "61f91d232e175937b960e213", + "notifications": [] + } + ], + "type": "publisher", + "notifications": [ + { + "notificationType": "dataAccessRequest", + "optIn": true, + "subscribedEmails": [ + "vijisrisan@gmail.com", + "hello@gmail.com" + ], + "_id": "6384dcce285c42274308a947" + } + ], + "__v": 369, + "createdAt": "2020-12-11T10:46:22.406Z", + "updatedAt": "2023-02-13T13:19:02.636Z" +}; +const mockResponse = [ + { + "notificationType": "dataAccessRequest", + "optIn": true, + "subscribedEmails": [ + { + "value": "vijisrisan@gmail.com", + "error": "" + }, + { + "value": "hello@gmail.com", + "error": "" + } + ] + } +]; + +export { + mockTeam, + mockResponse, +} \ No newline at end of file diff --git a/src/resources/utilities/__tests__/formatTeamNotifications.test.js b/src/resources/utilities/__tests__/formatTeamNotifications.test.js new file mode 100644 index 00000000..0c5f5028 --- /dev/null +++ b/src/resources/utilities/__tests__/formatTeamNotifications.test.js @@ -0,0 +1,13 @@ +import teamV3Util from '../team.v3.util'; +import { + mockTeam, + mockResponse, +} from '../__mocks__/formatTeamNotifications.mock'; + +describe("test formatTeamNotifications", () => { + it("should return return response", () => { + let response = teamV3Util.formatTeamNotifications(mockTeam); + expect(typeof response).toBe('object') + expect(response).toEqual(expect.arrayContaining(mockResponse)); + }); +}); \ No newline at end of file From 93a59bee628223ed88607b323e39d1e5fe0ceb54 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Feb 2023 12:30:32 +0000 Subject: [PATCH 137/229] optin checking in team notifications workflow --- src/resources/workflow/workflow.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 3fc4120c..5d4d4278 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -163,7 +163,7 @@ export default class WorkflowService { let { publisherObj, workflow = {}, actioner = '' } = context; custodianManagers = teamController.getTeamMembersByRole(publisherObj, 'All'); - if (publisherObj.notifications[0].optIn) { + if (has(publisherObj.notifications[0], 'optIn') && publisherObj.notifications[0].optIn) { publisherObj.notifications[0].subscribedEmails.map(teamEmail => { custodianManagers.push({ email: teamEmail }); }); From 295882d5fc1608040b3dc292e395327b761e0931 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Feb 2023 12:39:05 +0000 Subject: [PATCH 138/229] update perms dataset onboarding --- src/utils/datasetonboarding.util.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index ad90677b..377030da 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -68,9 +68,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { } if (!isEmpty(publisherTeam)) { - if (publisherTeam.roles.find(role => role.includes(constants.roleTypes.METADATA_EDITOR))) { - return { authorised: true, userType: constants.roleTypes.METADATA_EDITOR }; - } else if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { + if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_MANAGER }; } } From 0a110c964d495e048433c1e749c7b54cdd141d94 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 13:16:06 +0000 Subject: [PATCH 139/229] custodian.dar.manager is manager --- src/resources/utilities/constants.util.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index 9a77720f..677d36bc 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -360,7 +360,7 @@ const _rolesAcceptedByRoles = { 'custodian.team.admin', 'custodian.metadata.manager', 'metadata_editor', - 'custodian.dar.manager', + 'manager', 'reviewer', ], 'custodian.metadata.manager': [ @@ -370,8 +370,8 @@ const _rolesAcceptedByRoles = { 'metadata_editor': [ 'metadata_editor', ], - 'custodian.dar.manager': [ - 'custodian.dar.manager', + 'manager': [ + 'manager', 'reviewer', ], 'reviewer': [ From e7dfe4a9e6d28193b76e3b38a2bf413aa5145bec Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 13:26:56 +0000 Subject: [PATCH 140/229] update --- src/resources/utilities/constants.util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index 677d36bc..d1cfc298 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -370,8 +370,8 @@ const _rolesAcceptedByRoles = { 'metadata_editor': [ 'metadata_editor', ], - 'manager': [ - 'manager', + 'custodian.dar.manager': [ + 'custodian.dar.manager', 'reviewer', ], 'reviewer': [ From 70d5cdd99f61f233690ec83a2a3aa3af022d0dde Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 14:27:35 +0000 Subject: [PATCH 141/229] added custodian.dar.manager --- src/resources/utilities/constants.util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index d1cfc298..f62a876a 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -360,6 +360,7 @@ const _rolesAcceptedByRoles = { 'custodian.team.admin', 'custodian.metadata.manager', 'metadata_editor', + 'custodian.dar.manager', 'manager', 'reviewer', ], From 698415971be32473da1bd017410e2de7aabac2d4 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 14:51:26 +0000 Subject: [PATCH 142/229] update cloud build --- cloudbuild_dynamic.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudbuild_dynamic.yaml b/cloudbuild_dynamic.yaml index ebb677b2..4b96d097 100644 --- a/cloudbuild_dynamic.yaml +++ b/cloudbuild_dynamic.yaml @@ -30,10 +30,10 @@ steps: ] - name: 'node' args: ['npm', 'install'] - - name: 'node' - args: ['-r', 'esm', 'migrations/migrate.js', 'up', '--autosync', 'true'] - env: - - 'MIGRATE_dbConnectionUri=${_MIGRATE_DBCONNECTIONURI}' + # - name: 'node' + # args: ['-r', 'esm', 'migrations/migrate.js', 'up', '--autosync', 'true'] + # env: + # - 'MIGRATE_dbConnectionUri=${_MIGRATE_DBCONNECTIONURI}' - name: 'node' args: ['npm', 'test'] env: From ed42ab51c8c4f9bfc99c642458bc2fc516ce6f69 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 15:14:42 +0000 Subject: [PATCH 143/229] remove from build --- cloudbuild_dynamic.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cloudbuild_dynamic.yaml b/cloudbuild_dynamic.yaml index 4b96d097..85677726 100644 --- a/cloudbuild_dynamic.yaml +++ b/cloudbuild_dynamic.yaml @@ -30,10 +30,6 @@ steps: ] - name: 'node' args: ['npm', 'install'] - # - name: 'node' - # args: ['-r', 'esm', 'migrations/migrate.js', 'up', '--autosync', 'true'] - # env: - # - 'MIGRATE_dbConnectionUri=${_MIGRATE_DBCONNECTIONURI}' - name: 'node' args: ['npm', 'test'] env: From 768515779b697a3885f40fc04021ee8e89246f37 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Mon, 20 Feb 2023 15:41:16 +0000 Subject: [PATCH 144/229] Testing on dev --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5eb312c6..45c7a584 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 \ No newline at end of file +v0.0.1 From 1b44d03fadbfb9c62f6bf0ea3874fd27b7b7b20a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Feb 2023 16:06:22 +0000 Subject: [PATCH 145/229] update activitylog middleware --- src/middlewares/__tests__/activitylog.middleware.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/middlewares/__tests__/activitylog.middleware.test.js b/src/middlewares/__tests__/activitylog.middleware.test.js index 71d9cd3d..7f47cf2e 100644 --- a/src/middlewares/__tests__/activitylog.middleware.test.js +++ b/src/middlewares/__tests__/activitylog.middleware.test.js @@ -150,7 +150,6 @@ describe('Testing the ActivityLog middleware', () => { await authoriseView(req, res, nextFunction); expect(versionsStub.calledOnce).toBe(true); - expect(nextFunction.mock.calls.length).toBe(1); }); it('Should respond 401 if an error is thrown', async () => { From 917c12e863d8e3dc09c8300babfd6037c257b724 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 11:27:42 +0000 Subject: [PATCH 146/229] GAT-1887: Testing Slack Channel notifications --- .github/workflows/dev_deployment.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 849c12b3..630d767a 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -56,6 +56,15 @@ jobs: run: |- docker build -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} -t '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest ./ docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }} + + - name: Build Notification + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL' + message: Building {{ env.GITHUB_REF_NAME }} branch + if: always() # END - Docker auth and build @@ -131,4 +140,13 @@ jobs: with: args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + + - name: Deploy Notification + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL' + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() # If required, use the Cloud Run url output in later steps \ No newline at end of file From bb9a680693492de732b0188553b6dcd48c327339 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 11:39:40 +0000 Subject: [PATCH 147/229] GAT-1887: Testing Slack Channel notifications --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 630d767a..2f4d72aa 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -62,7 +62,7 @@ jobs: with: status: ${{ job.status }} steps: ${{ toJson(steps) }} - channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL' + channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' message: Building {{ env.GITHUB_REF_NAME }} branch if: always() # END - Docker auth and build @@ -146,7 +146,7 @@ jobs: with: status: ${{ job.status }} steps: ${{ toJson(steps) }} - channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL' + channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' message: Deploying {{ env.GITHUB_REF_NAME }} branch if: always() # If required, use the Cloud Run url output in later steps \ No newline at end of file From dbd6ba8bcc2ea10ac058d0e80eaa91356742b606 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 11:43:17 +0000 Subject: [PATCH 148/229] GAT-1887: Testing deployment --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 45c7a584..ae39fab3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 +v0.0.0 From df54f818b3d66d6c15f7c72b2362f08798c3bfa2 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 12:03:45 +0000 Subject: [PATCH 149/229] GAT-1887: Testing Slack Channel notifications --- .github/workflows/dev_deployment.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 2f4d72aa..798ab848 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -10,6 +10,8 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.GAR_LOCATION }}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: build: @@ -62,7 +64,7 @@ jobs: with: status: ${{ job.status }} steps: ${{ toJson(steps) }} - channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + channel: ${{ env.SLACK_CHANNEL }}' message: Building {{ env.GITHUB_REF_NAME }} branch if: always() # END - Docker auth and build From e0c260418e5f639b91e2765b55189a2096cf9855 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 12:04:11 +0000 Subject: [PATCH 150/229] GAT-1887: Testing deployment --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index ae39fab3..45c7a584 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 +v0.0.1 From 00d43abda9e1ebe2faa27e2561345a6e97e25c15 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 12:07:34 +0000 Subject: [PATCH 151/229] GAT-1887: Testing deployment --- .github/workflows/dev_deployment.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index 798ab848..c3d299b8 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -10,7 +10,7 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location - SLACK_WEBHOOK_URL: '${{ secrets.GAR_LOCATION }}' + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: @@ -64,7 +64,7 @@ jobs: with: status: ${{ job.status }} steps: ${{ toJson(steps) }} - channel: ${{ env.SLACK_CHANNEL }}' + channel: ${{ env.SLACK_CHANNEL }} message: Building {{ env.GITHUB_REF_NAME }} branch if: always() # END - Docker auth and build @@ -148,7 +148,7 @@ jobs: with: status: ${{ job.status }} steps: ${{ toJson(steps) }} - channel: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + channel: ${{ env.SLACK_CHANNEL }} message: Deploying {{ env.GITHUB_REF_NAME }} branch if: always() # If required, use the Cloud Run url output in later steps \ No newline at end of file From 87058fe10bce92b365debd905af2a69de1695410 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 12:22:36 +0000 Subject: [PATCH 152/229] GAT-1887: Testing Slack Channel notifications --- .github/workflows/dev_deployment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index c3d299b8..a4aaf2b5 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -42,6 +42,7 @@ jobs: service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' - name: Login to GAR + id: garlogin uses: docker/login-action@v2 with: registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.PROJECT_ID }} @@ -49,6 +50,7 @@ jobs: password: ${{ steps.auth.outputs.access_token }} - name: Build and Push Container + id: build shell: bash env: GAR_LOCATION: ${{ secrets.GAR_LOCATION }} @@ -60,6 +62,7 @@ jobs: docker push --all-tags '${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }} - name: Build Notification + id: buildnotificationsent uses: act10ns/slack@v1 with: status: ${{ job.status }} @@ -144,6 +147,7 @@ jobs: args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - name: Deploy Notification + id: deploynotificationsent uses: act10ns/slack@v1 with: status: ${{ job.status }} From c139aa85485bbc44c7eedcd9db63dba8a3a79b88 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Tue, 21 Feb 2023 12:23:17 +0000 Subject: [PATCH 153/229] GAT-1887: Testing deployment --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 45c7a584..ae39fab3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.1 +v0.0.0 From 21446b530a1c3bbe8b61be5b96b0e69e8403a871 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 21 Feb 2023 14:43:30 +0000 Subject: [PATCH 154/229] update darrequest util --- src/resources/datarequest/utils/datarequest.util.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index 1faea00e..3fe9b068 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -35,31 +35,33 @@ const injectQuestionActions = (jsonSchema, userType, applicationStatus, role = ' const getUserPermissionsForApplication = (application, userId, _id) => { let authorised = false, - userType = ''; + userType = '', + isTeamMember = false; if (!application || !userId || !_id) { throw new HttpExceptions(`User not authorized to perform this action`,403); } if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { - teamV3Util.checkUserRolesByTeam( + isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.datasets[0].publisher.team, _id ); } else if (has(application, 'publisherObj.team')) { - teamV3Util.checkUserRolesByTeam( + isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.publisherObj.team, _id ); } - if ((application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { + if (isTeamMember && (application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { userType = constants.userTypes.CUSTODIAN; authorised = true; } + // If user is not authenticated as a custodian, check if they are an author or the main applicant if (application.applicationStatus === constants.applicationStatuses.INPROGRESS || isEmpty(userType)) { if (application.userId === userId || (application.authorIds && application.authorIds.includes(userId))) { userType = constants.userTypes.APPLICANT; @@ -70,7 +72,7 @@ const getUserPermissionsForApplication = (application, userId, _id) => { if (authorised) { return userType; } - + throw new HttpExceptions(`User not authorized to perform this action`, 403); }; From 1919293a38fae4b82ac6264c859bc813a823e769 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 21 Feb 2023 16:53:27 +0000 Subject: [PATCH 155/229] GAT-1887: Slack Channel notifications --- .github/workflows/uat_deployment.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml index c4a1b1cb..88592c62 100644 --- a/.github/workflows/uat_deployment.yaml +++ b/.github/workflows/uat_deployment.yaml @@ -11,6 +11,8 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben. # catchsha: @@ -60,4 +62,14 @@ jobs: # Deployment please don't modify anything here as the infrastructure with: # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:{{ steps.catchsha.outputs.GITHUB_SHA}} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' # Functionality not supported by Github Actions one to ccheck back agin in the future - args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' \ No newline at end of file + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From 153c9e7992c2208c8e3cb29d2ffa4fb01e7e7bb4 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 21 Feb 2023 16:57:04 +0000 Subject: [PATCH 156/229] GAT-1887: Slack Channel notifications --- .github/workflows/preprod_deployment.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml index b5e08ed1..8aded9b9 100644 --- a/.github/workflows/preprod_deployment.yaml +++ b/.github/workflows/preprod_deployment.yaml @@ -10,6 +10,8 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben deploy: @@ -52,4 +54,14 @@ jobs: # Deployment please don't modify anything here as the infrastructure with: # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # If required, use the Cloud Run url output in later steps \ No newline at end of file + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From 35be6a95de4cbb5f043a49e7aa23753612bb80ca Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:00:45 +0000 Subject: [PATCH 157/229] GAT-1887: Slack Channel notifications --- .github/workflows/prod_deployment.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index 5152f649..f0c9a1e2 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -10,6 +10,8 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben deploy: @@ -42,7 +44,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: 'GAT Live Release - '${{ steps.getversion.outputs.version }} + automatic_release_tag: 'GAT Live Release - ''${{ steps.getversion.outputs.version }}' prerelease: false - name: Deploy to Cloud Run @@ -59,4 +61,14 @@ jobs: # Deployment please don't modify anything here as the infrastructure with: # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # If required, use the Cloud Run url output in later steps \ No newline at end of file + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From dd723b5fa4094287bb7994b468ced5a3ba28b08e Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:04:25 +0000 Subject: [PATCH 158/229] GAT-1887: Slack Channel notifications --- .github/workflows/prod_deployment.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index 5152f649..18e2f377 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -10,6 +10,8 @@ on: env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben deploy: @@ -42,7 +44,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: 'GAT Live Release - '${{ steps.getversion.outputs.version }} + automatic_release_tag: 'GAT Live Release -''${{ steps.getversion.outputs.version }}' prerelease: false - name: Deploy to Cloud Run @@ -59,4 +61,14 @@ jobs: # Deployment please don't modify anything here as the infrastructure with: # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' - # If required, use the Cloud Run url output in later steps \ No newline at end of file + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From 20cd4e20dbbf802502d8d1b85c6aa29bf080b1d9 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 22 Feb 2023 14:04:18 +0000 Subject: [PATCH 159/229] update checking user role --- .../datarequest/datarequest.controller.js | 25 +++++++++++++++---- .../datarequest/utils/datarequest.util.js | 10 ++++++-- .../publisher/publisher.controller.js | 5 +++- .../__tests__/checkUserRolesByTeam.test.js | 2 +- src/resources/utilities/team.v3.util.js | 2 +- src/resources/workflow/workflow.controller.js | 20 ++++++++++++--- 6 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index a3afc8a5..299e1162 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -596,7 +596,10 @@ export default class DataRequestController extends Controller { // Only a custodian manager can set the final status of an application const { team = {} } = accessRecord.publisherObj.toObject(); if (!_.isEmpty(team)) { - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserObjectId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserObjectId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } // Extract params from body @@ -1300,7 +1303,10 @@ export default class DataRequestController extends Controller { // 3. Check permissions of user is manager of associated team if (_.has(accessRecord.toObject(), 'publisherObj.team')) { let { team } = accessRecord.publisherObj; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } // 5. Check publisher allows workflows @@ -1375,7 +1381,10 @@ export default class DataRequestController extends Controller { // 3. Check permissions of user is manager of associated team if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } // 5. Check application is in review state @@ -1484,7 +1493,10 @@ export default class DataRequestController extends Controller { // 3. Check permissions of user is reviewer of associated team if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_REVIEWER], team.toObject(), requestingUserObjectId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_REVIEWER], team.toObject(), requestingUserObjectId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } // 5. Check application is in-review @@ -1623,7 +1635,10 @@ export default class DataRequestController extends Controller { // 3. Check permissions of user is reviewer of associated team if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } // 5. Check application is in submitted state diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index 3fe9b068..d376a937 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -43,17 +43,23 @@ const getUserPermissionsForApplication = (application, userId, _id) => { } if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { - isTeamMember = teamV3Util.checkUserRolesByTeam( + let authorised = isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.datasets[0].publisher.team, _id ); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } else if (has(application, 'publisherObj.team')) { - isTeamMember = teamV3Util.checkUserRolesByTeam( + let authorised = isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.publisherObj.team, _id ); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } } if (isTeamMember && (application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 90523504..5ff09088 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -127,7 +127,10 @@ export default class PublisherController extends Controller { publisher: { team }, } = workflows[0]; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } return res.status(200).json({ success: true, workflows }); } catch (err) { diff --git a/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js b/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js index 12881b5f..135a6e61 100644 --- a/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js +++ b/src/resources/utilities/__tests__/checkUserRolesByTeam.test.js @@ -32,7 +32,7 @@ describe('checkUserRolesByTeam test', () => { try { teamV3Util.checkUserRolesByTeam(mockArrayCheckRolesManagerRole, mockTeam, mockUserId); } catch (error) { - expect(error).toBeInstanceOf(HttpExceptions); + expect(response).toBe(false); } }); }); \ No newline at end of file diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index decdad0b..d1720d53 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -319,7 +319,7 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { } } - throw new HttpExceptions(`User not authorized to perform this action`,403); + return false; } const formatTeamNotifications = (team) => { diff --git a/src/resources/workflow/workflow.controller.js b/src/resources/workflow/workflow.controller.js index 3dff1127..8367abc6 100644 --- a/src/resources/workflow/workflow.controller.js +++ b/src/resources/workflow/workflow.controller.js @@ -46,7 +46,10 @@ export default class WorkflowController extends Controller { } // 2. Check the requesting user is a manager of the custodian team let { _id: userId } = req.user; - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } // 4. Build workflow response let { active, _id, id, workflowName, version, steps, applications = [] } = workflow.toObject(); @@ -102,7 +105,10 @@ export default class WorkflowController extends Controller { if (!publisherObj) { throw new HttpExceptions(`You must supply a valid publisher to create the workflow against`, 400); } - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisherObj.team.toObject(), userId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisherObj.team.toObject(), userId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } // 5. Create new workflow model const id = helper.generatedNumericId(); @@ -162,7 +168,10 @@ export default class WorkflowController extends Controller { if (!workflow) { throw new HttpExceptions(`Workflow not Found`, 404); } - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } // 4. Ensure there are no in-review DARs with this workflow const applications = await DataRequestModel.countDocuments({ @@ -249,7 +258,10 @@ export default class WorkflowController extends Controller { if (!workflow) { throw new HttpExceptions(`Workflow not Found`, 404); } - teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + let authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], workflow.publisher.team.toObject(), userId); + if (!authorised) { + throw new HttpExceptions(`User not authorized to perform this action`,403); + } // 4. Ensure there are no in-review DARs with this workflow const applications = await DataRequestModel.countDocuments({ From 5fdbac423285f0bfe9df886be9cfd4c706069022 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 22 Feb 2023 14:21:03 +0000 Subject: [PATCH 160/229] replace autorize with isTeamMember --- src/resources/datarequest/utils/datarequest.util.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index d376a937..452d868a 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -43,23 +43,17 @@ const getUserPermissionsForApplication = (application, userId, _id) => { } if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { - let authorised = isTeamMember = teamV3Util.checkUserRolesByTeam( + isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.datasets[0].publisher.team, _id ); - if (!authorised) { - throw new HttpExceptions(`User not authorized to perform this action`,403); - } } else if (has(application, 'publisherObj.team')) { - let authorised = isTeamMember = teamV3Util.checkUserRolesByTeam( + isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( [constants.roleMemberTeam.CUST_DAR_MANAGER], application.publisherObj.team, _id ); - if (!authorised) { - throw new HttpExceptions(`User not authorized to perform this action`,403); - } } if (isTeamMember && (application.applicationStatus !== constants.applicationStatuses.INPROGRESS || application.isShared)) { From 185e9693d149d7d2e6c7b25a40af376b588d1d8f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 23 Feb 2023 13:52:39 +0000 Subject: [PATCH 161/229] update checkUserRolesByTeam --- src/resources/datarequest/utils/datarequest.util.js | 5 +++-- src/resources/utilities/team.v3.util.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index 452d868a..63ca3403 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -42,15 +42,16 @@ const getUserPermissionsForApplication = (application, userId, _id) => { throw new HttpExceptions(`User not authorized to perform this action`,403); } + // constants.roleMemberTeam.CUST_DAR_MANAGER if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( - [constants.roleMemberTeam.CUST_DAR_MANAGER], + [], application.datasets[0].publisher.team, _id ); } else if (has(application, 'publisherObj.team')) { isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( - [constants.roleMemberTeam.CUST_DAR_MANAGER], + [], application.publisherObj.team, _id ); diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index d1720d53..37f0f216 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -307,12 +307,12 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { if (userMember) { let { roles = [] } = userMember; - if (!arrayCheckRoles.length) { + if (arrayCheckRoles.length === 0) { return true; } const check = roles.filter(element => arrayCheckRoles.includes(element)).length; - if (check) { + if (check || arrayCheckRoles.length === 0 || roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) { return true; } From e98054f4ffbf1facee89cf3ee8d214d5db1c5206 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 24 Feb 2023 12:11:50 +0000 Subject: [PATCH 162/229] checking_messages --- src/resources/utilities/team.v3.util.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 37f0f216..1e9270c8 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -312,7 +312,11 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { } const check = roles.filter(element => arrayCheckRoles.includes(element)).length; - if (check || arrayCheckRoles.length === 0 || roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) { + if (check) { + return true; + } + + if (roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) { return true; } From 6726317a759ba5a34266e0314bd88b7daf19f2a6 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Tue, 28 Feb 2023 13:11:02 +0000 Subject: [PATCH 163/229] GAT-2050: GAT Live Release - v4.0.0 --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index ae39fab3..857572fc 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v0.0.0 +v4.0.0 From 5940fa1151d7b01afe4924b1bdbc9361c962f023 Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 28 Feb 2023 13:57:58 +0000 Subject: [PATCH 164/229] GAT-2050: Update Release bug --- .github/workflows/prod_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index f0c9a1e2..50cd4717 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -44,7 +44,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: 'GAT Live Release - ''${{ steps.getversion.outputs.version }}' + automatic_release_tag: 'GAT Live Release - v4.0.0' prerelease: false - name: Deploy to Cloud Run From 0e0cbf4ae89bed4e440cd1f34b44073b52c6414c Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 28 Feb 2023 14:01:31 +0000 Subject: [PATCH 165/229] GAT-2050: Update release tag --- .github/workflows/prod_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index 50cd4717..bc2607a0 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -44,7 +44,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: 'GAT Live Release - v4.0.0' + automatic_release_tag: v4.0.0 prerelease: false - name: Deploy to Cloud Run From 8db46f602b958bc8a6034c47eba4948e313273db Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Tue, 28 Feb 2023 14:44:20 +0000 Subject: [PATCH 166/229] GAT-1887:Fix Tag bug --- .github/workflows/prod_deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml index bc2607a0..3c62899f 100644 --- a/.github/workflows/prod_deployment.yaml +++ b/.github/workflows/prod_deployment.yaml @@ -44,7 +44,7 @@ jobs: # Deployment please don't modify anything here as the infrastructure uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: v4.0.0 + automatic_release_tag: '${{ steps.getversion.outputs.version }}' prerelease: false - name: Deploy to Cloud Run From de6b6c2fbf3ead65f6ad4782af46d36a66484a76 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 1 Mar 2023 14:46:29 +0000 Subject: [PATCH 167/229] GAT-1887: Cleanup --- .github/workflows/preprod_deployment.yaml | 67 ++++++++++++++++++++ .github/workflows/prod_deployment.yaml | 74 +++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100755 .github/workflows/preprod_deployment.yaml create mode 100755 .github/workflows/prod_deployment.yaml diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml new file mode 100755 index 00000000..8aded9b9 --- /dev/null +++ b/.github/workflows/preprod_deployment.yaml @@ -0,0 +1,67 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'preprod' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-preprod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: preprod + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml new file mode 100755 index 00000000..3c62899f --- /dev/null +++ b/.github/workflows/prod_deployment.yaml @@ -0,0 +1,74 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'master' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-prod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: master + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Create the release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: '${{ steps.getversion.outputs.version }}' + prerelease: false + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From a6e552b76e3efda2235e1e1ac4b20d91ac6689cb Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 1 Mar 2023 14:52:15 +0000 Subject: [PATCH 168/229] GAT-1887: Cleanup --- .github/workflows/prod_deployment.yaml | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 .github/workflows/prod_deployment.yaml diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml new file mode 100755 index 00000000..3c62899f --- /dev/null +++ b/.github/workflows/prod_deployment.yaml @@ -0,0 +1,74 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'master' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-prod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: master + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Create the release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: '${{ steps.getversion.outputs.version }}' + prerelease: false + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From 052d5f4c89c88697a3ce97b44dab5bec817d82d6 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 1 Mar 2023 14:57:42 +0000 Subject: [PATCH 169/229] GAT-1887: Cleanup --- .github/workflows/preprod_deployment.yaml | 67 ++++++++++++++++++++ .github/workflows/prod_deployment.yaml | 74 ++++++++++++++++++++++ .github/workflows/uat_deployment.yaml | 75 +++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100755 .github/workflows/preprod_deployment.yaml create mode 100755 .github/workflows/prod_deployment.yaml create mode 100755 .github/workflows/uat_deployment.yaml diff --git a/.github/workflows/preprod_deployment.yaml b/.github/workflows/preprod_deployment.yaml new file mode 100755 index 00000000..8aded9b9 --- /dev/null +++ b/.github/workflows/preprod_deployment.yaml @@ -0,0 +1,67 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'preprod' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-preprod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: preprod + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() diff --git a/.github/workflows/prod_deployment.yaml b/.github/workflows/prod_deployment.yaml new file mode 100755 index 00000000..3c62899f --- /dev/null +++ b/.github/workflows/prod_deployment.yaml @@ -0,0 +1,74 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'master' + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben + deploy: + if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-prod + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: master + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + - name: Create the release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: '${{ steps.getversion.outputs.version }}' + prerelease: false + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:${{ github.sha }} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # If required, use the Cloud Run url output in later steps + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() diff --git a/.github/workflows/uat_deployment.yaml b/.github/workflows/uat_deployment.yaml new file mode 100755 index 00000000..88592c62 --- /dev/null +++ b/.github/workflows/uat_deployment.yaml @@ -0,0 +1,75 @@ +name: Deploy to Cloud Run + +on: + pull_request: + types: + - closed + branches: + - 'release' + + +env: + PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. + GAR_LOCATION: '${{ secrets.GAR_LOCATION }}' # TODO: update Artifact Registry location + SLACK_WEBHOOK_URL: '${{ secrets.SLACK_WEBHOOK_URL}}' + SLACK_CHANNEL: '${{ secrets.GITHUBACTIONS_SLACK_CHANNEL }}' + +jobs: # Deployment please don't modify anything here as the infrastructure is controlled by terraform any changes here please agree with chris and reuben. + # catchsha: + # uses: HDRUK/gateway-api/.github/workflows/dev_deployment.yaml@dev + deploy: + # if: github.event.pull_request.merged == true + permissions: + contents: write + id-token: write + + runs-on: ubuntu-latest + environment: legacy-uat + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: release + + - name: Google Auth + id: auth + uses: 'google-github-actions/auth@v0' + with: + token_format: 'access_token' + workload_identity_provider: '${{ secrets.WIF_PROVIDER }}' + service_account: '${{ secrets.WIF_SERVICE_ACCOUNT }}' + + - name: Read VERSION file + id: getversion + # run: echo "::set-output name=version::$(cat Chart.yaml)" + run: echo "version=$(cat Chart.yaml)" >> $GITHUB_OUTPUT + + # - name: Get SHA + # id: getsha + # run: echo ${{ needs.catchsha.outputs.GITHUB_SHA }} + + - name: Deploy to Cloud Run + uses: actions-hub/gcloud@master + id: deploy + env: + PROJECT_ID: ${{ secrets.PROJECT_ID }} + DEV_PROJECT_ID: ${{ secrets.DEV_PROJECT_ID }} + GAR_LOCATION: ${{ secrets.GAR_LOCATION }} + GAR_NAME: ${{ secrets.GAR_NAME_API }} + SERVICE_NAME: '${{ secrets.SERVICE_NAME_API }}' + SERVICE_REGION: '${{ secrets.SERVICE_REGION_API }}' + + with: + # args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:{{ steps.catchsha.outputs.GITHUB_SHA}} --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + # Functionality not supported by Github Actions one to ccheck back agin in the future + args: run services update '${{ env.SERVICE_NAME }}' --image='${{ env.GAR_LOCATION }}'-docker.pkg.dev/'${{ env.DEV_PROJECT_ID }}'/'${{ env.GAR_NAME }}'/${{ steps.getversion.outputs.version }}:latest --region='${{ env.SERVICE_REGION }}' --project='${{ env.PROJECT_ID }}' + + - name: Deploy Notification + id: deploynotificationsent + uses: act10ns/slack@v1 + with: + status: ${{ job.status }} + steps: ${{ toJson(steps) }} + channel: ${{ env.SLACK_CHANNEL }} + message: Deploying {{ env.GITHUB_REF_NAME }} branch + if: always() From 3a18170ea3129e98819f3605cc588041d74bb0d1 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 3 Mar 2023 09:00:26 +0000 Subject: [PATCH 170/229] update patch team endpoint --- Dockerfile.dev | 2 +- src/resources/team/v3/team.controller.js | 30 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Dockerfile.dev b/Dockerfile.dev index c3e780a0..f1a98dad 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:18 +FROM node:18.14.2 ENV GOOGLE_APPLICATION_CREDENTIALS="/usr/local/etc/gcloud/application_default_credentials.json" diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index df866731..de3d9c6d 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -157,8 +157,16 @@ class TeamController extends TeamService { const teamId = req.params.teamid; const updateUserId = req.params.memberid; const currentUserId = req.user._id; - const { roles = [] } = req.body; + const role = req.body; const allowPerms = req.allowPerms || []; + let tempRole = ''; + + if (Object.keys(role).length === 0) { + throw new HttpExceptions(`No Roles`, 400); + } + + let tempKeyRole = Object.keys(role)[0] || ''; + let tempValueRole = Object.values(role)[0] || ''; const currUserRoles = await this.checkUserAuth(teamId, currentUserId, allowPerms); @@ -172,14 +180,28 @@ class TeamController extends TeamService { } const approvedRoles = teamV3Util.listOfRolesAllowed(currUserRoles, constants.rolesAcceptedByRoles); - teamV3Util.checkAllowNewRoles(roles, approvedRoles); - + teamV3Util.checkAllowNewRoles([tempKeyRole], approvedRoles); team.members.map(member => { if (member.memberid.toString() === updateUserId.toString()) { - member.roles = roles; + tempRole = member.roles; + if (tempValueRole) { + tempRole.push(tempKeyRole); + } else { + tempRole = tempRole.filter(item => item !== tempKeyRole); + } + member.roles = [...new Set(tempRole)]; } }); + teamV3Util.checkIfExistAdminRole( + team.members, + [ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_MD_MANAGER + ] + ); + try { team.save(async err => { if (err) { From 84b6a4c46143b02fe797b54a86f188226778338e Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:28:47 +0000 Subject: [PATCH 171/229] GAT-2094: Disable 0/1 testing --- .github/workflows/dev_deployment.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index a4aaf2b5..60603063 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -4,8 +4,8 @@ on: push: branches: - 'dev' - paths: - - 'Chart.yaml' +# paths: +# - 'Chart.yaml' env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. @@ -155,4 +155,4 @@ jobs: channel: ${{ env.SLACK_CHANNEL }} message: Deploying {{ env.GITHUB_REF_NAME }} branch if: always() - # If required, use the Cloud Run url output in later steps \ No newline at end of file + # If required, use the Cloud Run url output in later steps From be437fa1aeaf1ebd66995186b939237394d8ce31 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Mar 2023 11:34:21 +0000 Subject: [PATCH 172/229] update counter for related objects with elements from datauserregister --- src/resources/search/search.repository.js | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index e241d15b..2cc2373b 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -17,6 +17,7 @@ import moment from 'moment'; import helperUtil from '../utilities/helper.util'; export async function getObjectResult(type, searchAll, searchQuery, startIndex, maxResults, sort, authorID, form) { + let collection = Data; if (type === 'course') { collection = Course; @@ -278,6 +279,33 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, as: 'relatedResourcesCourses', }, }, + { + $lookup: { + from: 'datauseregisters', + let: { + pid: '$pid', + }, + pipeline: [ + { $unwind: '$relatedObjects' }, + { + $match: { + $expr: { + $and: [ + { + $eq: ['$relatedObjects.pid', '$$pid'], + }, + { + $eq: ['$activeflag', 'active'], + }, + ], + }, + }, + }, + { $group: { _id: null, count: { $sum: 1 } } }, + ], + as: 'relatedResourcesDataUseRegister', + }, + }, { $project: { _id: 0, @@ -353,11 +381,19 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, else: { $first: '$relatedResourcesCourses.count' }, }, }, + { + $cond: { + if: { $eq: [{ $size: '$relatedResourcesDataUseRegister' }, 0] }, + then: 0, + else: { $first: '$relatedResourcesDataUseRegister.count' }, + }, + }, ], }, }, }, ]; + } else { queryObject = [ { $match: newSearchQuery }, From 6e3688f2d0aee3d8907ac5f60f91a09954386bb5 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Mar 2023 11:36:50 +0000 Subject: [PATCH 173/229] update chart.yml --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 857572fc..82f24fdf 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.0 +v4.0.1 From c0599aae38af11c29d0a0650d537fde0c5cc47ec Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Mar 2023 11:41:51 +0000 Subject: [PATCH 174/229] update chart.yml revert --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 82f24fdf..857572fc 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.1 +v4.0.0 From 91c7c8161e61221dfd19369caa022bb24ba596fb Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 8 Mar 2023 10:07:36 +0000 Subject: [PATCH 175/229] GAT-1887: Disable 0/1 testing --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index a4aaf2b5..eaa86c42 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -4,8 +4,8 @@ on: push: branches: - 'dev' - paths: - - 'Chart.yaml' + # paths: + # - 'Chart.yaml' env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. From 61a218788939b3a5a093da5e53cd59afd4406ba4 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 8 Mar 2023 10:16:20 +0000 Subject: [PATCH 176/229] GAT-1887: Disable 0/1 testing --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index a4aaf2b5..eaa86c42 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -4,8 +4,8 @@ on: push: branches: - 'dev' - paths: - - 'Chart.yaml' + # paths: + # - 'Chart.yaml' env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. From 448ef42379c3f8815a1c99570f5dcabf647b6e82 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Wed, 8 Mar 2023 10:18:36 +0000 Subject: [PATCH 177/229] GAT-1887: Disable 0/1 testing --- .github/workflows/dev_deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_deployment.yaml b/.github/workflows/dev_deployment.yaml index a4aaf2b5..eaa86c42 100644 --- a/.github/workflows/dev_deployment.yaml +++ b/.github/workflows/dev_deployment.yaml @@ -4,8 +4,8 @@ on: push: branches: - 'dev' - paths: - - 'Chart.yaml' + # paths: + # - 'Chart.yaml' env: PROJECT_ID: '${{ secrets.PROJECT_ID }}' # TODO: update Google Cloud project id. From d7696d8ef81acf9bf58d1a7d7674212613ecb726 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 8 Mar 2023 10:35:46 +0000 Subject: [PATCH 178/229] update chart yam GAT-2058 --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 857572fc..82f24fdf 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.0 +v4.0.1 From 588029b157167ad5ab92f90e88526786558fad5e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 10 Mar 2023 09:28:48 +0000 Subject: [PATCH 179/229] update --- src/resources/team/v3/team.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index de3d9c6d..a9f366b3 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -132,7 +132,7 @@ class TeamController extends TeamService { currentUserId, body: req.body, }, - output: users, + output: newMembers, }); team.members = team.members.concat(newMembers); From 8bf86f89291de98a364b49d64a77766cda6e172c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Mar 2023 15:27:00 +0000 Subject: [PATCH 180/229] update add team in a new team --- src/config/server.js | 4 ++-- src/resources/auth/utils.js | 6 +++++- src/resources/team/team.controller.js | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/config/server.js b/src/config/server.js index e3bfeb05..78d1225f 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -4,7 +4,7 @@ import express from 'express'; import Provider from 'oidc-provider'; import swaggerUi from 'swagger-ui-express'; import cors from 'cors'; -import logger from 'morgan'; +import morgan from 'morgan'; import passport from 'passport'; import cookieParser from 'cookie-parser'; import bodyParser from 'body-parser'; @@ -86,7 +86,7 @@ connectToDatabase(); app.use(bodyParser.json({ limit: '10mb', extended: true })); app.use(bodyParser.urlencoded({ limit: '10mb', extended: false })); -app.use(logger('dev')); +app.use(morgan('tiny')); app.use(cookieParser()); app.use(passport.initialize()); app.use(passport.session()); diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 13baaa87..628ce999 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -205,7 +205,11 @@ const loginAndSignToken = (req, res, next) => { const userIsTeamManager = () => async (req, res, next) => { const { user, params } = req; const members = await TeamModel.findOne({ _id: params.id }, { _id: 0, members: { $elemMatch: { memberid: user._id } } }).lean(); - if ((!isEmpty(members) && members.members[0].roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) || user.role === 'Admin') return next(); + + const isDarManager = members.members[0].roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER); + const isTeamAdmin = members.members[0].roles.includes(constants.roleMemberTeam.CUST_TEAM_ADMIN); + + if ((!isEmpty(members) && (isDarManager || isTeamAdmin)) || user.role === 'Admin') return next(); return res.status(401).json({ status: 'error', diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 6a508026..a0559f33 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -746,7 +746,7 @@ async function getManagerInfo(managerId, teamManagerIds, recipients) { ).exec(); teamManagerIds.push({ - roles: ['manager'], + roles: [constants.roleMemberTeam.CUST_TEAM_ADMIN], memberid: ObjectId(managerInfo._id.toString()), }); From 47430a0ecf5535b59005039c1c35bb04e2cdcd7e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 15 Mar 2023 16:34:46 +0000 Subject: [PATCH 181/229] update ammendaments --- .../amendment/amendment.controller.js | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/src/resources/datarequest/amendment/amendment.controller.js b/src/resources/datarequest/amendment/amendment.controller.js index 2347b26a..d465d55d 100644 --- a/src/resources/datarequest/amendment/amendment.controller.js +++ b/src/resources/datarequest/amendment/amendment.controller.js @@ -4,7 +4,8 @@ import constants from '../../utilities/constants.util'; import datarequestUtil from '../utils/datarequest.util'; import teamController from '../../team/team.controller'; import Controller from '../../base/controller'; -import { logger } from '../../utilities/logger'; +import HttpExceptions from '../../../exceptions/HttpExceptions'; +import teamV3Util from '../../utilities/team.v3.util'; const logCategory = 'Data Access Request'; @@ -25,6 +26,7 @@ export default class AmendmentController extends Controller { const requestingUserId = parseInt(req.user.id); const requestingUserObjectId = req.user._id; let { questionId, questionSetId, mode, reason, answer } = req.body; + if (_.isEmpty(questionId) || _.isEmpty(questionSetId)) { return res.status(400).json({ success: false, @@ -35,7 +37,7 @@ export default class AmendmentController extends Controller { // 2. Retrieve DAR from database const accessRecord = await this.dataRequestService.getApplicationWithTeamById(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. If application is not in review or submitted, amendments cannot be made @@ -43,18 +45,16 @@ export default class AmendmentController extends Controller { accessRecord.applicationStatus !== constants.applicationStatuses.SUBMITTED && accessRecord.applicationStatus !== constants.applicationStatuses.INREVIEW ) { - return res.status(400).json({ - success: false, - message: 'This application is not within a reviewable state and amendments cannot be made or requested at this time.', - }); + throw new HttpExceptions(`This application is not within a reviewable state and amendments cannot be made or requested at this time.`, 400); } // 4. Get the requesting users permission levels - let { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( + let userType = datarequestUtil.getUserPermissionsForApplication( accessRecord.toObject(), requestingUserId, requestingUserObjectId ); + let authorised = true; // 5. Get the current iteration amendment party let validParty = false; @@ -92,22 +92,18 @@ export default class AmendmentController extends Controller { // 7. Return unauthorised message if the user did not have sufficient access for action requested if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } // 8. Return bad request if the opposite party is editing the application if (!validParty) { - return res.status(400).json({ - status: 'failure', - message: 'You cannot make or request amendments to this application as the opposite party are currently responsible for it.', - }); + throw new HttpExceptions(`You cannot make or request amendments to this application as the opposite party are currently responsible for it.`, 400); } // 9. Save changes to database await accessRecord.save(async err => { if (err) { - console.error(err.message); - return res.status(500).json({ status: 'error', message: err.message }); + throw new HttpExceptions(err.message, 500); } else { // 10. Update json schema and question answers with modifications since original submission and retain previous version requested updates let accessRecordObj = accessRecord.toObject(); @@ -181,12 +177,8 @@ export default class AmendmentController extends Controller { } }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred updating the application amendment', - }); + process.stdout.write(err.message); + throw new HttpExceptions(`An error occurred updating the application amendment : ${err.message}`, 500); } } @@ -202,17 +194,17 @@ export default class AmendmentController extends Controller { let accessRecord = await this.dataRequestService.getApplicationForUpdateRequest(id); if (!accessRecord) { - return res.status(404).json({ status: 'error', message: 'Application not found.' }); + throw new HttpExceptions(`Application not found.`, 404); } // 3. Check permissions of user is manager of associated team let authorised = false; if (_.has(accessRecord.toObject(), 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team.toObject(), requestingUserObjectId); + authorised = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team.toObject(), requestingUserObjectId); } if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + throw new HttpExceptions(`Unauthorised`, 401); } // 4. Ensure single datasets are mapped correctly into array (backward compatibility for single dataset applications) @@ -223,19 +215,13 @@ export default class AmendmentController extends Controller { // 5. Get the current iteration amendment party and return bad request if the opposite party is editing the application const activeParty = this.amendmentService.getAmendmentIterationParty(accessRecord); if (activeParty !== constants.userTypes.CUSTODIAN) { - return res.status(400).json({ - status: 'failure', - message: 'You cannot make or request amendments to this application as the applicant(s) are amending the current version.', - }); + throw new HttpExceptions(`You cannot make or request amendments to this application as the applicant(s) are amending the current version.`, 400); } // 6. Check some amendments exist to be submitted to the applicant(s) const { unansweredAmendments } = this.amendmentService.countAmendments(accessRecord, constants.userTypes.CUSTODIAN); if (unansweredAmendments === 0) { - return res.status(400).json({ - status: 'failure', - message: 'You cannot submit requested amendments as none have been requested in the current version', - }); + throw new HttpExceptions(`You cannot submit requested amendments as none have been requested in the current version`, 400); } // 7. Find current amendment iteration index @@ -247,8 +233,7 @@ export default class AmendmentController extends Controller { // 9. Save changes to database await accessRecord.save(async err => { if (err) { - console.error(err.message); - return res.status(500).json({ status: 'error', message: err.message }); + throw new HttpExceptions(err.message, 500); } else { // 10. Send update request notifications let fullAccessRecord = await this.dataRequestService.getApplicationById(id); @@ -263,12 +248,7 @@ export default class AmendmentController extends Controller { } }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'An error occurred attempting to submit the requested updates', - }); + throw new HttpExceptions(`An error occurred attempting to submit the requested updates : ${err.message}`, 500); } } } From c729b5a255983f55e0665edbb29413b4f682fef4 Mon Sep 17 00:00:00 2001 From: Loki Date: Thu, 16 Mar 2023 09:13:44 +0000 Subject: [PATCH 182/229] updates hubspot calls to use new auth method for private apps --- src/services/hubspot/hubspot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/hubspot/hubspot.js b/src/services/hubspot/hubspot.js index c4054b2d..6e261f4f 100644 --- a/src/services/hubspot/hubspot.js +++ b/src/services/hubspot/hubspot.js @@ -10,7 +10,7 @@ import { logger } from '../../resources/utilities/logger'; const apiKey = process.env.HUBSPOT_API_KEY; const logCategory = 'Hubspot Integration'; let hubspotClient; -if (apiKey) hubspotClient = new Client({ apiKey, numberOfApiCallRetries: NumberOfRetries.Three }); +if (apiKey) hubspotClient = new Client({ accessToken: apiKey, numberOfApiCallRetries: NumberOfRetries.Three }); /** * Sync A Single Gateway User With Hubspot From 2bf60b6451c92def144bd1873ecb5ad003684666 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 09:41:20 +0000 Subject: [PATCH 183/229] update perms dar --- src/resources/datarequest/amendment/amendment.controller.js | 2 +- src/resources/datarequest/datarequest.controller.js | 2 +- src/resources/datarequest/utils/datarequest.util.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/datarequest/amendment/amendment.controller.js b/src/resources/datarequest/amendment/amendment.controller.js index d465d55d..0ff7288b 100644 --- a/src/resources/datarequest/amendment/amendment.controller.js +++ b/src/resources/datarequest/amendment/amendment.controller.js @@ -153,7 +153,7 @@ export default class AmendmentController extends Controller { } // 12. Append question actions depending on user type and application status - let userRole = activeParty === constants.userTypes.CUSTODIAN ? constants.roleTypes.MANAGER : ''; + let userRole = activeParty === constants.userTypes.CUSTODIAN ? constants.roleMemberTeam.CUST_DAR_MANAGER : ''; accessRecordObj.jsonSchema = datarequestUtil.injectQuestionActions( accessRecordObj.jsonSchema, userType, diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 299e1162..03625709 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -143,7 +143,7 @@ export default class DataRequestController extends Controller { // 9. Get role type for requesting user, applicable for only Custodian users i.e. Manager/Reviewer role const userRole = - userType === constants.userTypes.APPLICANT ? '' : isManager ? constants.roleTypes.MANAGER : constants.roleTypes.REVIEWER; + userType === constants.userTypes.APPLICANT ? '' : isManager ? constants.roleMemberTeam.CUST_DAR_MANAGER : constants.roleMemberTeam.CUST_DAR_REVIEWER; // 10. Handle amendment type application loading for Custodian showing any changes in the major version if (applicationType === constants.submissionTypes.AMENDED && userType === constants.userTypes.CUSTODIAN) { diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index 63ca3403..bf1a89fc 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -12,7 +12,7 @@ const injectQuestionActions = (jsonSchema, userType, applicationStatus, role = ' userType === constants.userTypes.CUSTODIAN && applicationStatus === constants.applicationStatuses.INREVIEW && activeParty === constants.userTypes.CUSTODIAN && - role === constants.roleTypes.MANAGER && + role === constants.roleMemberTeam.CUST_DAR_MANAGER && isLatestMinorVersion ) return { From 2b8b2e3e21b6f33624e7218759b38f33658af561 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 09:44:48 +0000 Subject: [PATCH 184/229] update perms dar for workflow --- src/resources/workflow/workflow.service.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 5d4d4278..e27d5089 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -5,6 +5,7 @@ import teamController from '../team/team.controller'; import constants from '../utilities/constants.util'; import emailGenerator from '../utilities/emailGenerator.util'; import notificationBuilder from '../utilities/notificationBuilder'; +import teamV3Util from '../utilities/team.v3.util'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); @@ -22,7 +23,8 @@ export default class WorkflowService { // Check if the current user can override the current step if (has(accessRecord, 'publisherObj.team')) { const { team } = accessRecord.publisherObj; - isManager = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserObjectId); + // isManager = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserObjectId); + isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], team, requestingUserObjectId); // Set the workflow override capability if there is an active step and user is a manager if (!isEmpty(workflow)) { workflow.canOverrideStep = !workflow.isCompleted && isManager; From 61dbe3b9c56d7a4e73784d6ce0d3decb2b008ed1 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 09:53:41 +0000 Subject: [PATCH 185/229] update perms dar for workflow --- src/resources/workflow/workflow.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index e27d5089..764c28e1 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -143,7 +143,7 @@ export default class WorkflowService { getReviewManagers(team, requestingUserId) { const { members = [], users = [] } = team; const managers = members.filter(mem => { - return mem.roles.includes('manager'); + return mem.roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER); }); return users .filter(user => managers.some(manager => manager.memberid.toString() === user._id.toString())) From d649126030710d610d158bfd0540417c203ee5ca Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 11:37:23 +0000 Subject: [PATCH 186/229] update widget perms --- src/resources/publisher/publisher.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/publisher/publisher.route.js b/src/resources/publisher/publisher.route.js index ef6dbd44..428a2da3 100644 --- a/src/resources/publisher/publisher.route.js +++ b/src/resources/publisher/publisher.route.js @@ -59,7 +59,7 @@ router.get( // @route PATCH /api/publishers/:id/dataUseWidget // @desc Update data use widget settings (terms and conditions) // @access Public -router.patch('/:id/dataUseWidget', passport.authenticate('jwt'), utils.userIsTeamManager(), (req, res) => +router.patch('/:id/dataUseWidget', passport.authenticate('jwt'), (req, res) => publisherController.updateDataUseWidget(req, res) ); router.patch('/dataRequestModalContent/:id', passport.authenticate('jwt'), utils.userIsTeamManager(), (req, res) => From 5977b99572317c4c876456692f762f56e12ed6e0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 12:15:24 +0000 Subject: [PATCH 187/229] update add new user in team --- Chart.yaml | 2 +- src/resources/team/team.controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 82f24fdf..bda368d5 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.1 +v4.0.2 diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index be01d022..29348d95 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -82,7 +82,7 @@ const formatTeamUsers = team => { id, _id, email, - additionalInfo: { organisation, bio, showOrganisation, showBio }, + additionalInfo: { organisation, bio, showOrganisation = true, showBio = true }, } = user; let userMember = team.members.find(el => el.memberid.toString() === user._id.toString()); let { roles = [] } = userMember; From 6eb4c008fa6ed57f95f42632095b9c9b8fa517b6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 12:18:06 +0000 Subject: [PATCH 188/229] update add new user in team --- src/resources/team/team.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 29348d95..3dd8d549 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -82,7 +82,7 @@ const formatTeamUsers = team => { id, _id, email, - additionalInfo: { organisation, bio, showOrganisation = true, showBio = true }, + additionalInfo: { organisation = '', bio = '', showOrganisation = true, showBio = true }, } = user; let userMember = team.members.find(el => el.memberid.toString() === user._id.toString()); let { roles = [] } = userMember; From 91227c53fdd88d65c515e98aa55d7bc3757356f2 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 16 Mar 2023 15:49:10 +0000 Subject: [PATCH 189/229] update --- src/resources/team/team.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 3dd8d549..d269a206 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -41,7 +41,6 @@ const getTeamById = async (req, res) => { // GET api/v1/teams/:id/members const getTeamMembers = async (req, res) => { try { - // 1. Get the team from the database const team = await TeamModel.findOne({ _id: req.params.id }).populate({ path: 'users', populate: { @@ -82,8 +81,9 @@ const formatTeamUsers = team => { id, _id, email, - additionalInfo: { organisation = '', bio = '', showOrganisation = true, showBio = true }, + additionalInfo, } = user; + let { organisation = '', bio = '', showOrganisation = true, showBio = true } = { ...additionalInfo }; let userMember = team.members.find(el => el.memberid.toString() === user._id.toString()); let { roles = [] } = userMember; return { From 24af278692f00ccf0e99c1809d527d9c1546e59e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 20 Mar 2023 08:26:47 +0000 Subject: [PATCH 190/229] cosmetic changes --- .../dataUseRegister.controller.js | 60 ++++--------------- .../dataUseRegister/dataUseRegister.route.js | 13 ++-- 2 files changed, 18 insertions(+), 55 deletions(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index 03903dfe..fc2ec01b 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -14,6 +14,7 @@ import { filtersService } from '../filters/dependency'; import { DataUseRegister } from '../dataUseRegister/dataUseRegister.model'; import { isEmpty, isUndefined } from 'lodash'; import { UserModel } from '../user/user.model'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const logCategory = 'dataUseRegister'; @@ -33,10 +34,7 @@ export default class DataUseRegisterController extends Controller { // If no id provided, it is a bad request if (!id) { - return res.status(400).json({ - success: false, - message: 'You must provide a dataUseRegister identifier', - }); + throw new HttpExceptions(`You must provide a dataUseRegister identifier`, 400); } // Find the dataUseRegister @@ -53,10 +51,7 @@ export default class DataUseRegisterController extends Controller { // Return if no dataUseRegister found if (!dataUseRegister) { - return res.status(404).json({ - success: false, - message: 'A dataUseRegister could not be found with the provided id', - }); + throw new HttpExceptions(`A dataUseRegister could not be found with the provided id`, 404); } // Reverse look up @@ -100,12 +95,7 @@ export default class DataUseRegisterController extends Controller { ...dataUseRegister, }); } catch (err) { - // Return error response if something goes wrong - console.error(err.message); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${err.message}`, 500); } } @@ -149,12 +139,7 @@ export default class DataUseRegisterController extends Controller { }); } } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${err.message}`, 500); } } @@ -210,12 +195,7 @@ export default class DataUseRegisterController extends Controller { success: true, }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${JSON.stringify(err.message)}`, 500); } } @@ -231,12 +211,7 @@ export default class DataUseRegisterController extends Controller { result, }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${JSON.stringify(err.message)}`, 500); } } @@ -248,12 +223,7 @@ export default class DataUseRegisterController extends Controller { return res.status(200).json({ success: true, result }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${err.message}`, 500); } } @@ -354,12 +324,7 @@ export default class DataUseRegisterController extends Controller { return res.status(200).json({ success: true, result }); } catch (err) { - //Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${err.message}`, 500); } } @@ -456,12 +421,7 @@ export default class DataUseRegisterController extends Controller { this.dataUseRegisterService.updateDataUseRegister(id, { counter }); return res.status(200).json({ success: true }); } catch (err) { - // Return error response if something goes wrong - logger.logError(err, logCategory); - return res.status(500).json({ - success: false, - message: 'A server error occurred, please try again', - }); + throw new HttpExceptions(`A server error occurred, please try again :: ${err.message}`, 500); } } } diff --git a/src/resources/dataUseRegister/dataUseRegister.route.js b/src/resources/dataUseRegister/dataUseRegister.route.js index a2ff75cf..fa2cfc52 100644 --- a/src/resources/dataUseRegister/dataUseRegister.route.js +++ b/src/resources/dataUseRegister/dataUseRegister.route.js @@ -15,7 +15,7 @@ router.get('/search', logger.logRequestMiddleware({ logCategory, action: 'Search dataUseRegisterController.searchDataUseRegisters(req, res) ); -// @route GET /api/v2/data-use-registers/id +// @route GET /api/v2/data-use-registers/:id // @desc Returns a dataUseRegister based on dataUseRegister ID provided // @access Public router.get('/:id', logger.logRequestMiddleware({ logCategory, action: 'Viewed dataUseRegister data' }), (req, res) => @@ -35,8 +35,9 @@ router.get( // @route PATCH /api/v2/data-use-registers/counter // @desc Updates the data use register counter for page views // @access Public -router.patch('/counter', logger.logRequestMiddleware({ logCategory, action: 'Data use counter update' }), (req, res) => - dataUseRegisterController.updateDataUseRegisterCounter(req, res) +router.patch('/counter', + logger.logRequestMiddleware({ logCategory, action: 'Data use counter update' }), + (req, res) => dataUseRegisterController.updateDataUseRegisterCounter(req, res) ); // @route PATCH /api/v2/data-use-registers/id @@ -54,8 +55,10 @@ router.patch( // @route POST /api/v2/data-use-registers/check // @desc Check the submitted data uses for duplicates and returns links to Gatway entities (datasets, users) // @access Public -router.post('/check', passport.authenticate('jwt'), logger.logRequestMiddleware({ logCategory, action: 'Check data uses' }), (req, res) => - dataUseRegisterController.checkDataUseRegister(req, res) +router.post('/check', + passport.authenticate('jwt'), + logger.logRequestMiddleware({ logCategory, action: 'Check data uses' }), + (req, res) => dataUseRegisterController.checkDataUseRegister(req, res) ); // @route POST /api/v2/data-use-registers/upload From f79029f64d31b65dda3bc986f7a2566235f44651 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 22 Mar 2023 14:22:37 +0000 Subject: [PATCH 191/229] send email assign and remove role for user --- src/resources/team/v3/team.controller.js | 21 +- src/resources/team/v3/util/emailTeam.js | 509 ++++++++++++++++++ .../utilities/emailGenerator.util.js | 23 + 3 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 src/resources/team/v3/util/emailTeam.js diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index a9f366b3..6e1763f1 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -10,6 +10,7 @@ import { TeamModel } from '../team.model'; import { LoggingService } from '../../../services'; import emailGenerator from '../../utilities/emailGenerator.util'; import notificationBuilder from '../../utilities/notificationBuilder'; +import emailTeam from './util/emailTeam'; class TeamController extends TeamService { _logger; @@ -202,6 +203,8 @@ class TeamController extends TeamService { ] ); + let teamClone = team; + try { team.save(async err => { if (err) { @@ -210,6 +213,8 @@ class TeamController extends TeamService { let updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); + await this.updateTeamMemberMessage(teamId, updateUserId, currentUserId, tempKeyRole, tempValueRole, teamClone); + this.sendLogInGoogle({ action: 'updateTeamMember', input: { @@ -225,13 +230,27 @@ class TeamController extends TeamService { success: true, members: users, }); + } - }); + }); + } catch (e) { throw new HttpExceptions(e.message); } } + async updateTeamMemberMessage(teamId, userId, currentUserId, permission, permissionStatus, team) { + const userDetails = await UserModel.findOne({ _id: userId }); + const userName = `${userDetails.firstname.toUpperCase()} ${userDetails.lastname.toUpperCase()}`; + const currentUserDetails = await UserModel.findOne({ _id: currentUserId }); + const currentUserName = `${currentUserDetails.firstname.toUpperCase()} ${currentUserDetails.lastname.toUpperCase()}`; + const publisherName = team.publisher.name.toUpperCase(); + const subjectEmail = emailTeam.subjectEmail(publisherName, currentUserName, permission, permissionStatus); + const bodyEmail = emailTeam.bodyEmail(publisherName, currentUserName, userName, permission, permissionStatus, teamId, team); + + await emailGenerator.sendEmailOne(userDetails.email, constants.hdrukEmail, subjectEmail, bodyEmail); + } + async getTeamNotifications(req, res) { const teamId = req.params.teamid; const currentUserId = req.user._id; diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js new file mode 100644 index 00000000..34839b8e --- /dev/null +++ b/src/resources/team/v3/util/emailTeam.js @@ -0,0 +1,509 @@ +import HttpExceptions from "../../../../exceptions/HttpExceptions"; +import constants from "../../../utilities/constants.util"; + +const subjectEmail = (teamName = '', userName = '', role, status) => { + let subject = ''; + let publisherName = ''; + + if (teamName.search('>') === -1) { + publisherName = teamName.trim(); + } else { + publisherName = teamName.split('>')[1].trim(); + } + + switch (role) { + case 'custodian.team.admin': + if (status) { + subject = `${userName} has added you to the ${publisherName} publishing team on the Gateway as a Team Admin`; + } else { + subject = `You have been removed as a Team Admin for the ${publisherName} team on the Gateway.`; + } + break; + case 'custodian.metadata.manager': + if (status) { + subject = `${userName} has added you to the ${publisherName} publishing team on the Gateway as a Metadata Manager`; + } else { + subject = `You have been removed as a Metadata Manager for the ${publisherName} team on the Gateway`; + } + break; + case 'metadata_editor': + if (status) { + subject = `${userName} has added you to the ${publisherName} publishing team on the Gateway as a Metadata Editor`; + } else { + subject = `You have been removed as a Metadata Editor for the ${publisherName} team on the Gateway.`; + } + break; + case 'custodian.dar.manager': + if (status) { + subject = `${userName} has added you to the ${publisherName} publishing team on the Gateway as a Data Access Manager`; + } else { + subject = `You have been removed as a Data Access Manager for the ${publisherName} team on the Gateway.`; + } + break; + case 'reviewer': + if (status) { + subject = `${userName} has added you to the ${publisherName} publishing team on the Gateway as a Reviewer`; + } else { + subject = `You have been removed as a Reviewer for the ${publisherName} team on the Gateway.`; + } + break; + default: + break; + } + + return subject; +} + +const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, status, teamId, team) => { + const urlHdrukLogoEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_logo_email.jpg'; + const urlHdrukHeaderEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_header_email.jpg'; + + let topBodyEmail = ''; + let middleBodyEmail = ''; + let footerBodyEmail = ''; + let bodyEmail = ''; + + let publisherName = ''; + + if (teamName.search('>') === -1) { + publisherName = teamName.trim(); + } else { + publisherName = teamName.split('>')[1].trim(); + } + const urlDatasetsTeam = `${process.env.homeURL}/search?search=&datasetpublisher=${publisherName}&datasetSort=latest&tab=Datasets`; + const urlDataAccessRequestTeam = `${process.env.homeURL}/account?tab=dataaccessrequests&teamType=team&teamId=${teamId}`; + const urlManageTeam = `${process.env.homeURL}/account?tab=teamManagement&teamType=team&teamId=${teamId}&subTab=members`; + + const teamAdmin = _generateTeamAdmin(team); + + topBodyEmail = ` + + +
+ + + +
+ + + + + `; + + switch (role) { + case 'custodian.team.admin': + if (status) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } else { + middleBodyEmail = ` + + + + + + + + + + + + + + + + `; + } + break; + case 'custodian.metadata.manager': + if (status) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } else { + middleBodyEmail = ` + + + + + + + + + + + + + + + + `; + } + break; + case 'metadata_editor': + if (status) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } else { + middleBodyEmail = ` + + + + + + + + + + + + + + + + `; + } + break; + case 'custodian.dar.manager': + if (status) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } else { + middleBodyEmail = ` + + + + + + + + + + + + + + + + `; + } + break; + case 'reviewer': + if (status) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } else { + middleBodyEmail = ` + + + + + + + + + + + + + + + + `; + } + break; + default: + break; + } + + let currentYear = new Date().getFullYear(); + footerBodyEmail = ` + + + + + + +
+ Health Data Gateway +
+ Custodian Team Admin has been assigned +
+ ${currentUserName} has added you to the ${publisherName} publishing team on the Gateway as a Team Admin. +
+ You can now add, remove, and change the roles of other members of the ${publisherName} team. +
+ Manage team +
+ Custodian Team Admin has been removed +
+ You have been removed as a Team Admin for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Add roles of other members of the ${publisherName} team.
  • +
  • Remove roles of other members of the ${publisherName} team.
  • +
  • Change the roles of other members of the ${publisherName} team.
  • +
+
+ You can no longer:
+
    +
  • Onboard and manage information about datasets uploaded by the ${publisherName} team.
  • +
  • Add and remove other team members with editor permissions.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ Metadata Manager has been assigned +
+ ${currentUserName} has added you the ${publisherName} publishing team on the Gateway as a Metadata Manager. +
+ You can now:
+
    +
  • Onboard and manage information about datasets uploaded by the ${publisherName} team.
  • +
  • Add and remove other team members with editor permissions.
  • +
+
+ View Datasets +
+ Metadata Manager has been removed +
+ You have been removed as a Metadata Manager for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Onboard and manage information about datasets uploaded by the ${publisherName} team.
  • +
  • Add and remove other team members with editor permissions.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ View Datasets +
+ Metadata Editor has been assigned +
+ ${currentUserName} has added you the ${publisherName} publishing team on the Gateway as a Metadata Editor. +
+ You can now:
+
    +
  • Onboard information about datasets uploaded by the ${publisherName} team.
  • +
  • Manage information about datasets uploaded by the ${publisherName} team.
  • +
+
+ View Datasets +
+ Metadata Editor has been removed +
+ You have been removed as a Metadata Editor for the ${publisherName} team on the Gateway. +
+ You can no longer Onboard and manage information about datasets uploaded by the ${publisherName} team. +
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ View Datasets +
+ DAR Manager has been assigned +
+ ${currentUserName} has added you the ${publisherName} publishing team on the Gateway as a Data Access Manager. +
+ You can now:
+
    +
  • Manage data access requests through the Gateway for the ${publisherName} team.
  • +
  • You can create and assign workflows, process applications, and communicate with applicants through the Gateway.
  • +
  • You can also add and remove other team members, and assign sections of the data access review workflow to them.
  • +
+
+ View data access requests +
+ DAR Manager has been removed +
+ You have been removed as a Data Access Manager for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Manage data access requests through the Gateway for the ${publisherName} team.
  • +
  • Create and assign workflows, process applications, and communicate with applicants through the Gateway.
  • +
  • Add and remove other team members.
  • +
  • Assign sections of the data access review workflow to them.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ View Datasets +
+ DAR Reviewer has been assigned +
+ ${currentUserName} has added you to the ${publisherName} publishing team on the Gateway as a Reviewer. +
+ You can now:
+
    +
  • Review sections of a data access request that have been assigned to you by a Data Access Manager for the ${publisherName} team.
  • +
  • You can process applications and communicate with applicants through the Gateway.
  • +
+
+ View data access requests +
+ DAR Reviewer has been removed +
+ You have been removed as a Reviewer Manager for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Review sections of a data access request that have been assigned to you by a Data Access Manager for the ${publisherName} team.
  • +
  • Process applications and communicate with applicants through the Gateway.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ View Datasets +
+ www.healthdatagateway.org +
+ ©HDR UK ${currentYear}. All rights reserved. +
+
+ `; + + bodyEmail = `${topBodyEmail}${middleBodyEmail}${footerBodyEmail}`; + + return bodyEmail; +} + +const _generateTeamAdmin = (team) => { + const { members, users } = team; + const adminRole = [ + constants.roleMemberTeam.CUST_TEAM_ADMIN, + ]; + let adminMemberIds = []; + let adminMemberNames = []; + + members.map(member => { + if ( member.roles.some(mem => adminRole.includes(mem)) ) { + return adminMemberIds.push(member.memberid.toString()); + } + }); + + users.map(user => { + let userId = user._id.toString(); + if (adminMemberIds.includes(userId)) { + return adminMemberNames.push(`${user.firstname} ${user.lastname}`); + } + }); + + return _generateTableTeamAdmin(adminMemberNames); +} + +const _generateTableTeamAdmin = (teamNames) => { + if (teamNames.length === 0) { + return ''; + } + + let top = ``; + let body = ``; + let tmp = ``; + + teamNames.map(team => { + tmp = ` + + `; + body = body + tmp; + }); + + let bottom = `
${team}
`; + + return `${top}${body}${bottom}`; +} + +export default { + subjectEmail, + bodyEmail, +} \ No newline at end of file diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index cfd68d19..c265aac9 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2590,6 +2590,28 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta } }; +const _sendEmailOne = async (to, from, subject, body, attachments = []) => { + let message = { + to: to, + from: from, + subject: subject, + html: body, + attachments, + }; + + // 4. Send email + try { + await transporter.sendMail(message, (error, info) => { + if (error) { + return console.log(error); + } + console.log('Email sent: ' + info.response); + }); + } catch (error) { + console.error(error.response.body); + } +}; + const _sendEmailSmtp = async message => { try { await transporter.sendMail(message, (error, info) => { @@ -2702,6 +2724,7 @@ const _deleteWordAttachmentTempFiles = async () => { export default { //General sendEmail: _sendEmail, + sendEmailOne: _sendEmailOne, sendIntroEmail: _sendIntroEmail, generateEmailFooter: _generateEmailFooter, generateAttachment: _generateAttachment, From bc077d10c8540f41070c13cd8113bcdd53c4dd78 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 22 Mar 2023 15:27:41 +0000 Subject: [PATCH 192/229] update for metadata editor --- src/utils/datasetonboarding.util.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 377030da..cdeffa60 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -50,7 +50,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(teams.filter(team => team.type === constants.teamTypes.ADMIN))) { isMetadataAdmin = teams .filter(team => team.type === constants.teamTypes.ADMIN) - .find(team => team.roles.includes(constants.roleMemberTeam.CUST_MD_MANAGER)); + .find(team => team.roles.includes(constants.roleTypes.ADMIN_DATASET)); } if (!isEmpty(isMetadataAdmin)) { @@ -70,6 +70,8 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(publisherTeam)) { if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_MANAGER }; + } else if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_EDITOR))) { + return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_EDITOR }; } } From 49424b5b114aa0b7eb9df847c84d65718e8c4e2c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 24 Mar 2023 09:55:34 +0000 Subject: [PATCH 193/229] remove url datasets from remove perms --- src/resources/team/v3/util/emailTeam.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index 34839b8e..9ee80c1e 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -235,11 +235,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta ${teamAdmin} - - - View Datasets - - `; } break; @@ -294,11 +289,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta ${teamAdmin} - - - View Datasets - - `; } break; @@ -360,11 +350,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta ${teamAdmin} - - - View Datasets - - `; } break; @@ -423,11 +408,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta ${teamAdmin} - - - View Datasets - - `; } break; From 29383febe5583f91baca50a7babc25b7d5ef92eb Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 24 Mar 2023 10:46:25 +0000 Subject: [PATCH 194/229] update remove team admin email --- src/resources/team/v3/util/emailTeam.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index 9ee80c1e..b13f0253 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -162,15 +162,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta - - - You can no longer:
-
    -
  • Onboard and manage information about datasets uploaded by the ${publisherName} team.
  • -
  • Add and remove other team members with editor permissions.
  • -
- - For more information, please contact a Team Admin for your team:
From 697a80b7bf22a7677a4716d01d7f828b84f8b418 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 31 Mar 2023 13:08:21 +0100 Subject: [PATCH 195/229] metadata last updated not displaying in Collection --- Chart.yaml | 2 +- src/resources/search/search.repository.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index bda368d5..7cc166d3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.2 +v4.0.3 diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index 2cc2373b..6361e206 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -364,7 +364,7 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, 'datasetfields.metadataquality.weighted_error_percent': 1, 'datasetfields.metadataquality.weighted_completeness_percent': 1, - latestUpdate: '$timestamps.updated', + latestUpdate: '$updatedAt', relatedresources: { $add: [ { From 628dce95d17bf604b5a47a736826302567152e36 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 6 Apr 2023 10:16:27 +0100 Subject: [PATCH 196/229] date convertion to iso --- Chart.yaml | 2 +- src/resources/collections/collections.service.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 7cc166d3..f05f17cf 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.3 +v4.0.4 diff --git a/src/resources/collections/collections.service.js b/src/resources/collections/collections.service.js index 1c5c13ed..a6ed0092 100644 --- a/src/resources/collections/collections.service.js +++ b/src/resources/collections/collections.service.js @@ -253,7 +253,7 @@ export default class CollectionsService { } } - let relatedObject = { ...data[0], updated: Date.parse(updated) }; + let relatedObject = { ...data[0], updated: new Date(Date.parse(updated)).toISOString() }; resolve(relatedObject); }); } From 182a4935df8f3ea0cce85a3ea747db8000342ea4 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Thu, 6 Apr 2023 14:20:22 +0100 Subject: [PATCH 197/229] Reverting chart.yaml v --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index f05f17cf..7cc166d3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.4 +v4.0.3 From 899fc6f2a1ff45b8b89407525c6e0f692c398371 Mon Sep 17 00:00:00 2001 From: reubensamuel Date: Thu, 20 Apr 2023 09:17:01 +0100 Subject: [PATCH 198/229] GAT-2138: Remove Cloudbuild references --- cloudbuild_dynamic.yaml | 42 ----------------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 cloudbuild_dynamic.yaml diff --git a/cloudbuild_dynamic.yaml b/cloudbuild_dynamic.yaml deleted file mode 100644 index 85677726..00000000 --- a/cloudbuild_dynamic.yaml +++ /dev/null @@ -1,42 +0,0 @@ -steps: - - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: ['-c', 'docker pull gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT} || exit 0'] - - name: 'gcr.io/cloud-builders/docker' - args: - [ - 'build', - '-t', - 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', - '--cache-from', - 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', - '.', - ] - - name: 'gcr.io/cloud-builders/docker' - args: ['push', 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}'] - - name: 'gcr.io/cloud-builders/gcloud' - args: - [ - 'run', - 'deploy', - '${_ENVIRONMENT}-api', - '--image', - 'gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT}', - '--platform', - 'managed', - '--region', - '${_REGION}', - '--allow-unauthenticated', - ] - - name: 'node' - args: ['npm', 'install'] - - name: 'node' - args: ['npm', 'test'] - env: - - 'URL=https://${_TEST_URL}' - -images: - - gcr.io/$PROJECT_ID/${_APP_NAME}:${_ENVIRONMENT} -timeout: 1200s -options: - machineType: 'E2_HIGHCPU_8' From 92c5933a25d714a45bd7d1209b7a2154e2e5bf03 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 24 Apr 2023 15:46:10 +0100 Subject: [PATCH 199/229] update chart to 5.0.0 --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 7cc166d3..d3845ad3 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v4.0.3 +v5.0.0 From 20e6233868a9ae96e7f13cd804ebef31a57daf63 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Thu, 27 Apr 2023 14:32:25 +0100 Subject: [PATCH 200/229] Adding missing imports --- src/resources/utilities/team.v3.util.js | 32 ++++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/resources/utilities/team.v3.util.js b/src/resources/utilities/team.v3.util.js index 1e9270c8..15bb2f9f 100644 --- a/src/resources/utilities/team.v3.util.js +++ b/src/resources/utilities/team.v3.util.js @@ -1,4 +1,4 @@ -import _, { isEmpty, has, isNull } from 'lodash'; +import _, { isEmpty, has, includes, isNull, filter, some } from 'lodash'; import constants from './constants.util'; // import emailGenerator from '../../utilities/emailGenerator.util'; import emailGenerator from './emailGenerator.util'; @@ -18,7 +18,7 @@ const checkTeamV3Permissions = (role, team, userId) => { let { members } = team; let userMember = members.find(el => el.memberid.toString() === userId.toString()); - if (userMember) { + if (userMember) { let { roles = [] } = userMember; if (roles.includes(role) || roles.includes(constants.roleTypes.MANAGER) || role === '') { return true; @@ -232,7 +232,7 @@ const checkIfLastManager = (members, deleteUserId) => { if (managerCount === 0) { throw new HttpExceptions(`You cannot delete the last manager in the team.`); } -} +}; const checkIfExistAdminRole = (members, roles) => { let checkingMemberRoles; @@ -250,7 +250,7 @@ const checkIfExistAdminRole = (members, roles) => { } return true; -} +}; const getAllRolesForApproverUser = (team, teamId, userId) => { let arrayRoles = []; @@ -274,7 +274,7 @@ const getAllRolesForApproverUser = (team, teamId, userId) => { }); return [...new Set(arrayRoles)]; -} +}; const listOfRolesAllowed = (userRoles, rolesAcceptedByRoles) => { let allowedRoles = []; @@ -285,8 +285,8 @@ const listOfRolesAllowed = (userRoles, rolesAcceptedByRoles) => { } }); - return [... new Set(allowedRoles)]; -} + return [...new Set(allowedRoles)]; +}; const checkAllowNewRoles = (userUpdateRoles, allowedRoles) => { userUpdateRoles.forEach(uRole => { @@ -319,17 +319,15 @@ const checkUserRolesByTeam = (arrayCheckRoles, team, userId) => { if (roles.includes(constants.roleMemberTeam.CUST_DAR_MANAGER)) { return true; } - } } return false; -} +}; -const formatTeamNotifications = (team) => { +const formatTeamNotifications = team => { let { notifications = [] } = team; if (!isEmpty(notifications)) { - return [...notifications].reduce((arr, notification) => { let teamNotificationEmails = []; let { notificationType = '', optIn = false, subscribedEmails = [] } = notification; @@ -402,11 +400,11 @@ const getMemberDetails = (memberIds = [], users = []) => { }; export default { - checkTeamV3Permissions, - checkIfAdmin, - formatTeamMembers, - createTeamNotifications, - getTeamName, + checkTeamV3Permissions, + checkIfAdmin, + formatTeamMembers, + createTeamNotifications, + getTeamName, checkUserAuthorization, checkingUserAuthorization, checkIfLastManager, @@ -419,4 +417,4 @@ export default { findMissingOptIns, filterMembersByNoticationTypes, getMemberDetails, -} \ No newline at end of file +}; From f32655b4c9cf12cf3d636c15d296a3387ff9bd30 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Tue, 9 May 2023 15:01:52 +0100 Subject: [PATCH 201/229] Removing permission as accessible to all --- src/resources/team/v3/team.route.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/resources/team/v3/team.route.js b/src/resources/team/v3/team.route.js index 57eff283..89efac8e 100644 --- a/src/resources/team/v3/team.route.js +++ b/src/resources/team/v3/team.route.js @@ -62,9 +62,6 @@ router.patch( router.get( '/:teamid/notifications', passport.authenticate('jwt'), - checkAccessToTeamMiddleware([ - constants.roleMemberTeam.CUST_TEAM_ADMIN - ]), (req, res) => TeamController.getTeamNotifications(req, res), ); From 990efef3337af5819e105e97e2f4855fc4c0d9e6 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Tue, 9 May 2023 15:56:15 +0100 Subject: [PATCH 202/229] Passing team id to FE --- src/resources/workflow/workflow.service.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 764c28e1..30fcdfb6 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -171,7 +171,8 @@ export default class WorkflowService { }); } managerUserIds = custodianManagers.map(user => user.id); - let { workflowName = 'Workflow Title', _id, steps, createdAt } = workflow; + let { workflowName = 'Workflow Title', _id, steps, createdAt, publisher } = workflow; + const action = type.replace('Workflow', '').toLowerCase(); options = { actioner, @@ -191,7 +192,8 @@ export default class WorkflowService { managerUserIds, `A new workflow of ${workflowName} has been created`, 'workflow', - _id + _id, + publisher ); // 5. Generate the email html = await emailGenerator.generateWorkflowActionEmail(options); @@ -206,7 +208,8 @@ export default class WorkflowService { managerUserIds, `A workflow of ${workflowName} has been updated`, 'workflow', - _id + _id, + publisher ); // 5. Generate the email html = await emailGenerator.generateWorkflowActionEmail(options); @@ -221,7 +224,8 @@ export default class WorkflowService { managerUserIds, `A workflow of ${workflowName} has been deleted`, 'workflow', - _id + _id, + publisher ); // 5. Generate the email html = await emailGenerator.generateWorkflowActionEmail(options); From 95ada3a0be6322904bd55378eee7b8b012af8f32 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 11 May 2023 14:47:50 +0100 Subject: [PATCH 203/229] reviewer can see dashboard --- src/resources/publisher/publisher.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 5ff09088..f0236d3a 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -70,7 +70,7 @@ export default class PublisherController extends Controller { } //Check if current user is a manager - const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisher.team, requestingUserId); + const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER, constants.roleMemberTeam.CUST_DAR_REVIEWER], publisher.team, requestingUserId); // 4. Find all applications for current team member view const applications = await this.publisherService.getPublisherDataAccessRequests(id, requestingUserId, isManager).catch(err => { From 2ef9bcb99c31eea8b786bb5d473f33036f6cd854 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 11 May 2023 14:52:06 +0100 Subject: [PATCH 204/229] update --- src/resources/publisher/publisher.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index f0236d3a..ed053608 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -73,6 +73,7 @@ export default class PublisherController extends Controller { const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER, constants.roleMemberTeam.CUST_DAR_REVIEWER], publisher.team, requestingUserId); // 4. Find all applications for current team member view + // if I am custodian.dar.manager, reviewer const applications = await this.publisherService.getPublisherDataAccessRequests(id, requestingUserId, isManager).catch(err => { logger.logError(err, logCategory); }); From 8c4660b455f022baac6b809338d7e6b431802d79 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 12 May 2023 14:10:28 +0100 Subject: [PATCH 205/229] rollback GAT-2406 --- src/resources/datarequest/utils/datarequest.util.js | 4 ++-- src/resources/publisher/publisher.controller.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/datarequest/utils/datarequest.util.js b/src/resources/datarequest/utils/datarequest.util.js index bf1a89fc..81579611 100644 --- a/src/resources/datarequest/utils/datarequest.util.js +++ b/src/resources/datarequest/utils/datarequest.util.js @@ -44,13 +44,13 @@ const getUserPermissionsForApplication = (application, userId, _id) => { // constants.roleMemberTeam.CUST_DAR_MANAGER if (has(application, 'datasets') && has(application.datasets[0], 'publisher.team')) { - isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( + isTeamMember = teamV3Util.checkUserRolesByTeam( [], application.datasets[0].publisher.team, _id ); } else if (has(application, 'publisherObj.team')) { - isTeamMember = isTeamMember = teamV3Util.checkUserRolesByTeam( + isTeamMember = teamV3Util.checkUserRolesByTeam( [], application.publisherObj.team, _id diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index ed053608..856e8336 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -70,7 +70,7 @@ export default class PublisherController extends Controller { } //Check if current user is a manager - const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER, constants.roleMemberTeam.CUST_DAR_REVIEWER], publisher.team, requestingUserId); + const isManager = teamV3Util.checkUserRolesByTeam([constants.roleMemberTeam.CUST_DAR_MANAGER], publisher.team, requestingUserId); // 4. Find all applications for current team member view // if I am custodian.dar.manager, reviewer From 9ec3e520ccee915918b168c68ad990aaf6af7db9 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 12 May 2023 17:02:52 +0100 Subject: [PATCH 206/229] create a new method who generate exception --- src/resources/datarequest/datarequest.route.js | 2 +- src/resources/utilities/logger.js | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/resources/datarequest/datarequest.route.js b/src/resources/datarequest/datarequest.route.js index dc435c82..87765f49 100644 --- a/src/resources/datarequest/datarequest.route.js +++ b/src/resources/datarequest/datarequest.route.js @@ -307,7 +307,7 @@ router.post( router.get( '/prepopulate-contributors/:id', passport.authenticate('jwt'), - logger.logRequestMiddleware({ logCategory, action: 'Get additional information for Data Access Request contributors' }), + logger.logRequestMiddlewareExceptions({ logCategory, action: 'Get additional information for Data Access Request contributors' }), (req, res) => dataRequestController.getContributorsAdditionalInfo(req, res) ); diff --git a/src/resources/utilities/logger.js b/src/resources/utilities/logger.js index 0be172e2..f28bf77d 100644 --- a/src/resources/utilities/logger.js +++ b/src/resources/utilities/logger.js @@ -1,4 +1,5 @@ import constants from './constants.util'; +import HttpExceptions from '../../exceptions/HttpExceptions'; const logRequestMiddleware = options => { return (req, res, next) => { @@ -8,6 +9,15 @@ const logRequestMiddleware = options => { }; }; +const logRequestMiddlewareExceptions = options => { + return (req, res, next) => { + const { logCategory, action } = options; + logger.logUserActivity(req.user, logCategory, constants.logTypes.USER, { action }); + throw new HttpExceptions(action); + next(); + }; +}; + const logSystemActivity = options => { const { category = 'Action not categorised', action = 'Action not described' } = options; process.stdout.write(`logSystemActivity : action ${action}, category ${category}`); @@ -30,6 +40,7 @@ const logError = (err, category) => { export const logger = { logRequestMiddleware, + logRequestMiddlewareExceptions, logSystemActivity, logUserActivity, logError, From d7e876978a75d5d4ef9d32514b3027fd55bdf877 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 15 May 2023 08:24:06 +0100 Subject: [PATCH 207/229] GAT-2128 rollback logRequestMiddleware --- src/resources/datarequest/datarequest.route.js | 2 +- src/resources/utilities/logger.js | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/resources/datarequest/datarequest.route.js b/src/resources/datarequest/datarequest.route.js index 87765f49..dc435c82 100644 --- a/src/resources/datarequest/datarequest.route.js +++ b/src/resources/datarequest/datarequest.route.js @@ -307,7 +307,7 @@ router.post( router.get( '/prepopulate-contributors/:id', passport.authenticate('jwt'), - logger.logRequestMiddlewareExceptions({ logCategory, action: 'Get additional information for Data Access Request contributors' }), + logger.logRequestMiddleware({ logCategory, action: 'Get additional information for Data Access Request contributors' }), (req, res) => dataRequestController.getContributorsAdditionalInfo(req, res) ); diff --git a/src/resources/utilities/logger.js b/src/resources/utilities/logger.js index f28bf77d..0be172e2 100644 --- a/src/resources/utilities/logger.js +++ b/src/resources/utilities/logger.js @@ -1,5 +1,4 @@ import constants from './constants.util'; -import HttpExceptions from '../../exceptions/HttpExceptions'; const logRequestMiddleware = options => { return (req, res, next) => { @@ -9,15 +8,6 @@ const logRequestMiddleware = options => { }; }; -const logRequestMiddlewareExceptions = options => { - return (req, res, next) => { - const { logCategory, action } = options; - logger.logUserActivity(req.user, logCategory, constants.logTypes.USER, { action }); - throw new HttpExceptions(action); - next(); - }; -}; - const logSystemActivity = options => { const { category = 'Action not categorised', action = 'Action not described' } = options; process.stdout.write(`logSystemActivity : action ${action}, category ${category}`); @@ -40,7 +30,6 @@ const logError = (err, category) => { export const logger = { logRequestMiddleware, - logRequestMiddlewareExceptions, logSystemActivity, logUserActivity, logError, From f7a134fd59322dd98cb62424b86e30c0fd9f5ba7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 15 May 2023 11:03:49 +0100 Subject: [PATCH 208/229] update perms --- src/utils/datasetonboarding.util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 16c9486b..cb8b4135 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -940,7 +940,7 @@ const createNotifications = async (type, context) => { .lean(); for (let member of team.members) { - if (member.roles.some(role => ['manager', 'metadata_editor'].includes(role))) teamMembers.push(member.memberid); + if (member.roles.some(role => ['metadata_editor'].includes(role))) teamMembers.push(member.memberid); } // 2. Create user notifications notificationBuilder.triggerNotificationMessage( From 5d516de829389aaf4bb5fe7f8f0ac15bcd99db66 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 15 May 2023 13:44:05 +0100 Subject: [PATCH 209/229] update notifications for metadata editor --- src/resources/utilities/notificationBuilder.js | 1 + src/utils/datasetonboarding.util.js | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/resources/utilities/notificationBuilder.js b/src/resources/utilities/notificationBuilder.js index ba68ea37..f5f0f120 100644 --- a/src/resources/utilities/notificationBuilder.js +++ b/src/resources/utilities/notificationBuilder.js @@ -3,6 +3,7 @@ import { MessagesModel } from '../message/message.model'; const triggerNotificationMessage = (messageRecipients, messageDescription, messageType, messageObjectID, publisherName = '') => { messageRecipients.forEach(async recipient => { let messageID = parseInt(Math.random().toString().replace('0.', '')); + let message = new MessagesModel({ messageType, messageSent: Date.now(), diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index cb8b4135..2d2d7988 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -66,7 +66,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(teams.find(team => team._id.toString() === publisherId))) { publisherTeam = teams.find(team => team._id.toString() === publisherId); } - + // console.log('publisherTeam : ' . JSON.stringify(publisherTeam)); if (!isEmpty(publisherTeam)) { if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_MANAGER }; @@ -932,16 +932,22 @@ const createNotifications = async (type, context) => { const isFederated = !_.isUndefined(team.publisher.federation) && team.publisher.federation.active; for (let member of team.members) { - teamMembers.push(member.memberid); + if ((Array.isArray(member.roles) && member.roles.some(role => ['manager', 'metadata_editor'].includes(role))) + || (typeof member.roles === 'string' && ['manager', 'metadata_editor'].includes(member.roles))) { + teamMembers.push(member.memberid); + } } teamMembersDetails = await UserModel.find({ _id: { $in: teamMembers } }) .populate('additionalInfo') .lean(); - for (let member of team.members) { - if (member.roles.some(role => ['metadata_editor'].includes(role))) teamMembers.push(member.memberid); + console.log('team.members : ' + JSON.stringify(team.members, 0, null) + '\n'); + + for (let member of teamMembersDetails) { + teamMembersIds.push(member.id); } + // 2. Create user notifications notificationBuilder.triggerNotificationMessage( teamMembersIds, From 4299840e22675621fcd050ad55c2e91c951692c5 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 15 May 2023 13:45:57 +0100 Subject: [PATCH 210/229] update --- src/utils/datasetonboarding.util.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 2d2d7988..3c7b454e 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -66,7 +66,7 @@ const getUserPermissionsForDataset = async (id, user, publisherId) => { if (!isEmpty(teams.find(team => team._id.toString() === publisherId))) { publisherTeam = teams.find(team => team._id.toString() === publisherId); } - // console.log('publisherTeam : ' . JSON.stringify(publisherTeam)); + if (!isEmpty(publisherTeam)) { if (publisherTeam.roles.find(role => role.includes(constants.roleMemberTeam.CUST_MD_MANAGER))) { return { authorised: true, userType: constants.roleMemberTeam.CUST_MD_MANAGER }; @@ -942,8 +942,6 @@ const createNotifications = async (type, context) => { .populate('additionalInfo') .lean(); - console.log('team.members : ' + JSON.stringify(team.members, 0, null) + '\n'); - for (let member of teamMembersDetails) { teamMembersIds.push(member.id); } From 0f87d45d048887b83472bb8993b44480849b3169 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 15 May 2023 17:17:55 +0100 Subject: [PATCH 211/229] update email for rejected datasets --- src/resources/utilities/emailGenerator.util.js | 2 +- src/utils/datasetonboarding.util.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 374d4d31..34ddbee9 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2147,7 +2147,7 @@ const _generateMetadataOnboardingRejected = options => { ${commentHTML} - View dataset dashboard + View dataset dashboard diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 3c7b454e..a66d8d58 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -947,14 +947,14 @@ const createNotifications = async (type, context) => { } // 2. Create user notifications - notificationBuilder.triggerNotificationMessage( - teamMembersIds, - context.datasetVersion !== '1.0.0' - ? `Your dataset version for "${context.name}" has been reviewed and rejected` - : `A dataset "${context.name}" has been reviewed and rejected`, - 'dataset rejected', - context.datasetv2.summary.publisher.identifier - ); + // notificationBuilder.triggerNotificationMessage( + // teamMembersIds, + // context.datasetVersion !== '1.0.0' + // ? `Your dataset version for "${context.name}" has been reviewed and rejected` + // : `A dataset "${context.name}" has been reviewed and rejected`, + // 'dataset rejected', + // context.datasetv2.summary.publisher.identifier + // ); // 3. Create email options = { name: context.name, From 7bd6853cfeae46bd4bfd63687fe912fb56330e11 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Thu, 18 May 2023 10:29:05 +0100 Subject: [PATCH 212/229] Updating email urls --- .../utilities/emailGenerator.util.js | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 34ddbee9..591cbf24 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -646,7 +646,7 @@ const _displayDARLink = accessId => { const _displayActivityLogLink = (accessId, publisher) => { if (!accessId) return ''; - const activityLogLink = `${process.env.homeURL}/account?tab=dataaccessrequests&team=${publisher}&id=${accessId}`; + const activityLogLink = `${process.env.homeURL}/account?tab=dataaccessrequests&teamType=team&teamId=${publisher}&id=${accessId}`; return `View activity log`; }; @@ -658,7 +658,7 @@ const _displayDataUseRegisterLink = dataUseId => { }; const _displayDataUseRegisterDashboardLink = () => { - const dataUseLink = `${process.env.homeURL}/account?tab=datause&team=admin`; + const dataUseLink = `${process.env.homeURL}/account?tab=datause&teamType=admin`; return `View all data uses for review `; }; @@ -1745,7 +1745,7 @@ const _generateRemovedFromTeam = options => { }; const _displayViewEmailNotifications = publisherId => { - let link = `${process.env.homeURL}/account?tab=teamManagement&innertab=notifications&team=${publisherId}`; + let link = `${process.env.homeURL}/account?tab=teamManagement&innertab=notifications&teamType=team&teamId=${publisherId}`; return ` @@ -2088,7 +2088,7 @@ const _generateMetadataOnboardingApproved = options => { ${commentHTML} @@ -2215,7 +2215,7 @@ const _generateMetadataOnboardingDuplicated = options => { @@ -2577,43 +2577,43 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta process.stdout.write(`Email sent: ${info.response}`); }); } catch (error) { - process.stdout.write(`EMAIL GENERATOR - _sendEmail : ${error.message}\n`); + process.stdout.write(`EMAIL GENERATOR - _sendEmail : ${error.message}\n`); } } }; const _sendEmailOne = async (to, from, subject, body, attachments = []) => { - let message = { - to: to, - from: from, - subject: subject, - html: body, - attachments, - }; - - // 4. Send email - try { - await transporter.sendMail(message, (error, info) => { - if (error) { - return console.log(error); - } - console.log('Email sent: ' + info.response); - }); - } catch (error) { - console.error(error.response.body); - } + let message = { + to: to, + from: from, + subject: subject, + html: body, + attachments, + }; + + // 4. Send email + try { + await transporter.sendMail(message, (error, info) => { + if (error) { + return console.log(error); + } + console.log('Email sent: ' + info.response); + }); + } catch (error) { + console.error(error.response.body); + } }; const _sendEmailSmtp = async message => { try { await transporter.sendMail(message, (error, info) => { if (error) { - return process.stdout.write(`${error.message}\n`);; + return process.stdout.write(`${error.message}\n`); } process.stdout.write(`Email sent: ${info.response}`); }); } catch (error) { - process.stdout.write(`EMAIL GENERATOR - _sendEmailSmtp : ${error.message}\n`); + process.stdout.write(`EMAIL GENERATOR - _sendEmailSmtp : ${error.message}\n`); } }; @@ -2710,7 +2710,7 @@ const _deleteWordAttachmentTempFiles = async () => { export default { //General sendEmail: _sendEmail, - sendEmailOne: _sendEmailOne, + sendEmailOne: _sendEmailOne, sendIntroEmail: _sendIntroEmail, generateEmailFooter: _generateEmailFooter, generateAttachment: _generateAttachment, From d73d61c343654a2f65fa1df3c8f20fb58eda3a8c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 24 May 2023 15:33:40 +0100 Subject: [PATCH 213/229] update counter in collection and dateset view without updatedat field --- src/resources/collections/collectioncounter.route.js | 2 +- src/resources/tool/counter.route.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/collections/collectioncounter.route.js b/src/resources/collections/collectioncounter.route.js index 52e8628c..d359fc9e 100644 --- a/src/resources/collections/collectioncounter.route.js +++ b/src/resources/collections/collectioncounter.route.js @@ -12,7 +12,7 @@ const datasetLimiter = rateLimit({ router.post('/update', datasetLimiter, async (req, res) => { const { id, counter } = req.body; - Collections.findOneAndUpdate({ id: { $eq: id } }, { counter }, err => { + Collections.findOneAndUpdate({ id: { $eq: id } }, { $set: { counter: counter } }, { timestamps: false }, err => { if (err) return res.json({ success: false, error: err }); return res.json({ success: true }); }); diff --git a/src/resources/tool/counter.route.js b/src/resources/tool/counter.route.js index eda04cd6..18e1fc5f 100644 --- a/src/resources/tool/counter.route.js +++ b/src/resources/tool/counter.route.js @@ -7,12 +7,12 @@ router.post('/update', async (req, res) => { const { id, counter } = req.body; if (isNaN(id)) { - Data.findOneAndUpdate({ datasetid: { $eq: id } }, { counter }, err => { + Data.findOneAndUpdate({ datasetid: { $eq: id } }, { $set: { counter: counter } }, { timestamps: false }, err => { if (err) return res.json({ success: false, error: err }); return res.json({ success: true }); }); } else { - Data.findOneAndUpdate({ id: { $eq: id } }, { counter }, err => { + Data.findOneAndUpdate({ id: { $eq: id } }, { $set: { counter: counter } }, { timestamps: false }, err => { if (err) return res.json({ success: false, error: err }); return res.json({ success: true }); }); From 9c01d607f1426c2f3384fd3a42b814e5f628053e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 1 Jun 2023 09:03:39 +0100 Subject: [PATCH 214/229] fix email for add user and remove user from team --- src/resources/team/v3/team.controller.js | 20 +++++++------ src/resources/team/v3/util/emailTeam.js | 37 ++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 6e1763f1..b6dc0cac 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -56,7 +56,7 @@ class TeamController extends TeamService { await this.checkUserAuth(teamId, currentUserId, allowPerms); const team = await this.getTeamByTeamId(teamId); - + let { members = [], users = [] } = team; let updatedMembers = [...members].filter(mem => mem.memberid.toString() !== deleteUserId.toString()); @@ -84,13 +84,13 @@ class TeamController extends TeamService { }); team.members = updatedMembers; + let teamClone = team; try { - team.save(function (err, result) { + team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); + await this.updateTeamMemberMessage(teamId, deleteUserId, currentUserId.toString(), '', null, teamClone, true); return res.status(204).json({ success: true, @@ -137,12 +137,14 @@ class TeamController extends TeamService { }); team.members = team.members.concat(newMembers); + let teamClone = team; team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let newUsers = await UserModel.find({ _id: memberId }); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); + for (const role of roles) { + await this.updateTeamMemberMessage(teamId, memberId[0], currentUserId.toString(), role, true, teamClone); + } const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); @@ -239,14 +241,14 @@ class TeamController extends TeamService { } } - async updateTeamMemberMessage(teamId, userId, currentUserId, permission, permissionStatus, team) { + async updateTeamMemberMessage(teamId, userId, currentUserId, permission, permissionStatus, team, deleteUser = false) { const userDetails = await UserModel.findOne({ _id: userId }); const userName = `${userDetails.firstname.toUpperCase()} ${userDetails.lastname.toUpperCase()}`; const currentUserDetails = await UserModel.findOne({ _id: currentUserId }); const currentUserName = `${currentUserDetails.firstname.toUpperCase()} ${currentUserDetails.lastname.toUpperCase()}`; const publisherName = team.publisher.name.toUpperCase(); - const subjectEmail = emailTeam.subjectEmail(publisherName, currentUserName, permission, permissionStatus); - const bodyEmail = emailTeam.bodyEmail(publisherName, currentUserName, userName, permission, permissionStatus, teamId, team); + const subjectEmail = emailTeam.subjectEmail(publisherName, currentUserName, permission, permissionStatus, deleteUser); + const bodyEmail = emailTeam.bodyEmail(publisherName, currentUserName, userName, permission, permissionStatus, teamId, team, deleteUser); await emailGenerator.sendEmailOne(userDetails.email, constants.hdrukEmail, subjectEmail, bodyEmail); } diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index b13f0253..2eb2e72a 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -1,7 +1,7 @@ import HttpExceptions from "../../../../exceptions/HttpExceptions"; import constants from "../../../utilities/constants.util"; -const subjectEmail = (teamName = '', userName = '', role, status) => { +const subjectEmail = (teamName = '', userName = '', role, status, deleteUser) => { let subject = ''; let publisherName = ''; @@ -47,14 +47,17 @@ const subjectEmail = (teamName = '', userName = '', role, status) => { subject = `You have been removed as a Reviewer for the ${publisherName} team on the Gateway.`; } break; - default: + default: + if (deleteUser) { + subject = `You have been removed as a user for the ${publisherName} team on the Gateway.`; + } break; } return subject; } -const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, status, teamId, team) => { +const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, status, teamId, team, deleteUser) => { const urlHdrukLogoEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_logo_email.jpg'; const urlHdrukHeaderEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_header_email.jpg'; @@ -403,6 +406,34 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } break; default: + if (deleteUser) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } break; } From 4a2eee7414ed5d9f74f9d68cbdee15655f52d578 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 1 Jun 2023 09:28:32 +0100 Subject: [PATCH 215/229] fix update email delete and create user team --- src/resources/team/v3/team.controller.js | 84 +++++++++++------------- src/resources/team/v3/util/emailTeam.js | 43 ++++++++++-- 2 files changed, 77 insertions(+), 50 deletions(-) diff --git a/src/resources/team/v3/team.controller.js b/src/resources/team/v3/team.controller.js index 6e1763f1..013eb399 100644 --- a/src/resources/team/v3/team.controller.js +++ b/src/resources/team/v3/team.controller.js @@ -43,7 +43,7 @@ class TeamController extends TeamService { res.status(200).json({ members, - }); + }); } async deleteTeamMember(req, res) { @@ -65,14 +65,14 @@ class TeamController extends TeamService { } teamV3Util.checkIfExistAdminRole( - updatedMembers, + updatedMembers, [ - constants.roleMemberTeam.CUST_TEAM_ADMIN, - constants.roleMemberTeam.CUST_DAR_MANAGER, + constants.roleMemberTeam.CUST_TEAM_ADMIN, + constants.roleMemberTeam.CUST_DAR_MANAGER, constants.roleMemberTeam.CUST_MD_MANAGER ] ); - + this.sendLogInGoogle({ action: 'deleteTeamMember', input: { @@ -84,19 +84,19 @@ class TeamController extends TeamService { }); team.members = updatedMembers; + let teamClone = team; try { - team.save(function (err, result) { + team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let removedUser = users.find(user => user._id.toString() === deleteUserId.toString()); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERREMOVED, { removedUser }, team, userObj); - + await this.updateTeamMemberMessage(teamId, deleteUserId, currentUserId.toString(), '', null, teamClone, true); + return res.status(204).json({ success: true, - }); + }); } - }); + }); } catch (e) { throw new HttpExceptions(e.message); @@ -137,12 +137,14 @@ class TeamController extends TeamService { }); team.members = team.members.concat(newMembers); + let teamClone = team; team.save(async err => { if (err) { throw new HttpExceptions(err.message); } else { - let newUsers = await UserModel.find({ _id: memberId }); - teamV3Util.createTeamNotifications(constants.notificationTypes.MEMBERADDED, { newUsers }, team, req.user); + for (const role of roles) { + await this.updateTeamMemberMessage(teamId, memberId[0], currentUserId.toString(), role, true, teamClone); + } const updatedTeam = await this.getMembersByTeamId(teamId); let users = teamV3Util.formatTeamMembers(updatedTeam); @@ -160,7 +162,7 @@ class TeamController extends TeamService { const currentUserId = req.user._id; const role = req.body; const allowPerms = req.allowPerms || []; - let tempRole = ''; + let tempRole = ''; if (Object.keys(role).length === 0) { throw new HttpExceptions(`No Roles`, 400); @@ -236,17 +238,17 @@ class TeamController extends TeamService { } catch (e) { throw new HttpExceptions(e.message); - } + } } - async updateTeamMemberMessage(teamId, userId, currentUserId, permission, permissionStatus, team) { + async updateTeamMemberMessage(teamId, userId, currentUserId, permission, permissionStatus, team, deleteUser = false) { const userDetails = await UserModel.findOne({ _id: userId }); const userName = `${userDetails.firstname.toUpperCase()} ${userDetails.lastname.toUpperCase()}`; const currentUserDetails = await UserModel.findOne({ _id: currentUserId }); const currentUserName = `${currentUserDetails.firstname.toUpperCase()} ${currentUserDetails.lastname.toUpperCase()}`; const publisherName = team.publisher.name.toUpperCase(); - const subjectEmail = emailTeam.subjectEmail(publisherName, currentUserName, permission, permissionStatus); - const bodyEmail = emailTeam.bodyEmail(publisherName, currentUserName, userName, permission, permissionStatus, teamId, team); + const subjectEmail = emailTeam.subjectEmail(publisherName, currentUserName, permission, permissionStatus, deleteUser); + const bodyEmail = emailTeam.bodyEmail(publisherName, currentUserName, userName, permission, permissionStatus, teamId, team, deleteUser); await emailGenerator.sendEmailOne(userDetails.email, constants.hdrukEmail, subjectEmail, bodyEmail); } @@ -308,10 +310,10 @@ class TeamController extends TeamService { user: { _id }, body: data, } = req; - + let { members, users, notifications } = team; let authorised = false; - + if (members) { authorised = [...members].some(el => el.memberid.toString() === _id.toString()); } @@ -319,19 +321,19 @@ class TeamController extends TeamService { if (!authorised) return res.status(401).json({ success: false }); let member = [...members].find(el => el.memberid.toString() === _id.toString()); - + let isManager = true; - + let { memberNotifications = [], teamNotifications = [] } = data; - + let missingOptIns = {}; - + if (!isEmpty(memberNotifications) && !isEmpty(teamNotifications)) { missingOptIns = teamV3Util.findMissingOptIns(memberNotifications, teamNotifications); } - + if (!isEmpty(missingOptIns)) return res.status(400).json({ success: false, message: missingOptIns }); - + if (isManager) { const optedOutTeamNotifications = Object.values([...teamNotifications]).filter(notification => !notification.optIn) || []; if (!isEmpty(optedOutTeamNotifications)) { @@ -350,10 +352,10 @@ class TeamController extends TeamService { }); }); } - + if (!isEmpty(notifications)) { let manager = [...users].find(user => user._id.toString() === member.memberid.toString()); - + [...notifications].forEach(dbNotification => { let { notificationType } = dbNotification; const notificationPayload = @@ -380,9 +382,8 @@ class TeamController extends TeamService { ...options, notificationRemoved: true, disabled: !payLoadOptIn ? true : false, - header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' - } generic team email address(es)`, + header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + } generic team email address(es)`, emailAddresses: dbOptIn && !payLoadOptIn ? payLoadSubscribedEmails : removedEmails, publisherId: team.publisher._id.toString(), }; @@ -390,17 +391,15 @@ class TeamController extends TeamService { emailGenerator.sendEmail( memberEmails, constants.hdrukEmail, - `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' } generic team email address(es)`, html, true ); - + notificationBuilder.triggerNotificationMessage( [...userIds], - `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${dbOptIn && !payLoadOptIn ? 'disabled all' : 'removed a' } generic team email address(es)`, 'team', team.publisher ? team.publisher.name : 'Undefined' @@ -411,9 +410,8 @@ class TeamController extends TeamService { options = { ...options, notificationRemoved: false, - header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' - } generic team email address(es)`, + header: `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${!dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + } generic team email address(es)`, emailAddresses: payLoadSubscribedEmails, publisherId: team.publisher._id.toString(), }; @@ -421,17 +419,15 @@ class TeamController extends TeamService { emailGenerator.sendEmail( memberEmails, constants.hdrukEmail, - `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${!dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' } generic team email address(es)`, html, true ); - + notificationBuilder.triggerNotificationMessage( [...userIds], - `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${ - !dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' + `A manager for ${team.publisher ? team.publisher.name : 'a team'} has ${!dbOptIn && payLoadOptIn ? 'enabled all' : 'added a' } generic team email address(es)`, 'team', team.publisher ? team.publisher.name : 'Undefined' diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index b13f0253..c594560f 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -1,7 +1,7 @@ import HttpExceptions from "../../../../exceptions/HttpExceptions"; import constants from "../../../utilities/constants.util"; -const subjectEmail = (teamName = '', userName = '', role, status) => { +const subjectEmail = (teamName = '', userName = '', role, status, deleteUser) => { let subject = ''; let publisherName = ''; @@ -47,20 +47,23 @@ const subjectEmail = (teamName = '', userName = '', role, status) => { subject = `You have been removed as a Reviewer for the ${publisherName} team on the Gateway.`; } break; - default: + default: + if (deleteUser) { + subject = `You have been removed as a user for the ${publisherName} team on the Gateway.`; + } break; } return subject; } -const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, status, teamId, team) => { +const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, status, teamId, team, deleteUser) => { const urlHdrukLogoEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_logo_email.jpg'; const urlHdrukHeaderEmail = 'https://storage.googleapis.com/public_files_dev/hdruk_header_email.jpg'; let topBodyEmail = ''; - let middleBodyEmail = ''; - let footerBodyEmail = ''; + let middleBodyEmail = ''; + let footerBodyEmail = ''; let bodyEmail = ''; let publisherName = ''; @@ -403,6 +406,34 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } break; default: + if (deleteUser) { + middleBodyEmail = ` + + + + + + + + + + + + + `; + } break; } @@ -438,7 +469,7 @@ const _generateTeamAdmin = (team) => { let adminMemberNames = []; members.map(member => { - if ( member.roles.some(mem => adminRole.includes(mem)) ) { + if (member.roles.some(mem => adminRole.includes(mem))) { return adminMemberIds.push(member.memberid.toString()); } }); From 981578d9ce7f51f7e471dfa00b0ac3c50f944a8d Mon Sep 17 00:00:00 2001 From: reubensamuel <110553227+reubensamuel@users.noreply.github.com> Date: Thu, 1 Jun 2023 09:44:55 +0100 Subject: [PATCH 216/229] GAT-2475: Bugfix for IAM --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index d3845ad3..4bccc27e 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.0 +v5.0.1 From 3095fb1b45e5fa7a4c00a7cd2e89fa045aaacc18 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 1 Jun 2023 12:18:45 +0100 Subject: [PATCH 217/229] update email format for delete user from team --- src/resources/team/v3/util/emailTeam.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index c594560f..afd54412 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -418,14 +418,6 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta You have been removed as a user for the ${publisherName} team on the Gateway. - - - From 3ca54e65433be0af3a279ebb68ac1ccc9fd34bf5 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 5 Jun 2023 08:37:39 +0100 Subject: [PATCH 220/229] update email --- src/resources/team/v3/util/emailTeam.js | 176 ++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 11 deletions(-) diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index 523f4ead..d9ee07ab 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -123,8 +123,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (status) { middleBodyEmail = ` - @@ -146,8 +160,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } else { middleBodyEmail = ` - @@ -178,8 +206,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (status) { middleBodyEmail = ` - @@ -205,8 +247,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } else { middleBodyEmail = ` - @@ -236,8 +292,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (status) { middleBodyEmail = ` - @@ -263,8 +333,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } else { middleBodyEmail = ` - @@ -290,8 +374,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (status) { middleBodyEmail = ` - @@ -318,8 +416,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } else { middleBodyEmail = ` - @@ -351,8 +463,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (status) { middleBodyEmail = ` - @@ -378,8 +504,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta } else { middleBodyEmail = ` - @@ -409,8 +549,22 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta if (deleteUser) { middleBodyEmail = ` - From 3ce8aad53346ccbff79b704f292119fdca6f5398 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 5 Jun 2023 08:47:56 +0100 Subject: [PATCH 221/229] update --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 5f81b6ab..7a674d87 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.3 +v5.0.4 From 8ba9a566b9b67f6e42b5b5489926853523d1e6b0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 5 Jun 2023 18:16:52 +0100 Subject: [PATCH 222/229] update worflow notifications --- src/resources/workflow/workflow.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 30fcdfb6..0d6d86f5 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -164,7 +164,7 @@ export default class WorkflowService { // deconstruct context let { publisherObj, workflow = {}, actioner = '' } = context; - custodianManagers = teamController.getTeamMembersByRole(publisherObj, 'All'); + custodianManagers = teamController.getTeamMembersByRole(publisherObj, constants.roleMemberTeam.CUST_DAR_MANAGER); if (has(publisherObj.notifications[0], 'optIn') && publisherObj.notifications[0].optIn) { publisherObj.notifications[0].subscribedEmails.map(teamEmail => { custodianManagers.push({ email: teamEmail }); From 062fa1afadc784b1d27618e4ac23708164ae3092 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 5 Jun 2023 18:30:29 +0100 Subject: [PATCH 223/229] update dar perms --- .../datarequest/datarequest.controller.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 4108bf27..d9163217 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -1844,7 +1844,7 @@ export default class DataRequestController extends Controller { switch (type) { case constants.notificationTypes.INPROGRESS: - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianManagersIds = custodianManagers.map(user => user.id); notificationTeam = accessRecord.publisherObj.team.notifications; if (notificationTeam.length && notificationTeam[0].optIn) { @@ -1884,7 +1884,7 @@ export default class DataRequestController extends Controller { // 1. Create notifications // Custodian manager and current step reviewer notifications // Retrieve all custodian manager user Ids and active step reviewers - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); let activeStep = this.workflowService.getActiveWorkflowStep(workflow); stepReviewers = this.workflowService.getStepReviewers(activeStep); // Create custodian notification @@ -1956,7 +1956,7 @@ export default class DataRequestController extends Controller { // Custodian notification if (_.has(accessRecord.datasets[0], 'publisher.team.users') && accessRecord.datasets[0].publisher.allowAccessRequestManagement) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, constants.roleMemberTeam.CUST_DAR_MANAGER); // check if publisher.team has email notifications custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( @@ -1976,7 +1976,7 @@ export default class DataRequestController extends Controller { } } else if (_.has(accessRecord, 'publisherObj') && accessRecord.publisherObj.allowAccessRequestManagement) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); // check if publisher.team has email notifications custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( @@ -2089,7 +2089,7 @@ export default class DataRequestController extends Controller { // Custodian notification if (_.has(accessRecord.datasets[0], 'publisher.team.users')) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2107,7 +2107,7 @@ export default class DataRequestController extends Controller { } } else if (_.has(accessRecord, 'publisherObj') && accessRecord.publisherObj.allowAccessRequestManagement) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( @@ -2326,7 +2326,7 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.FINALDECISIONREQUIRED: // 1. Get managers for publisher - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianManagersIds = custodianManagers.map(user => user.id); // 2. Create manager notifications @@ -2364,7 +2364,7 @@ export default class DataRequestController extends Controller { ); break; case constants.notificationTypes.DEADLINEWARNING: - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianManagersIds = custodianManagers.map(user => user.id); notificationTeam = accessRecord.publisherObj.team.notifications; if (notificationTeam.length && notificationTeam[0].optIn) { @@ -2407,7 +2407,7 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.DEADLINEPASSED: // 1. Get all managers - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianManagersIds = custodianManagers.map(user => user.id); // 2. Combine managers and reviewers remaining let deadlinePassedUserIds = [...remainingReviewerUserIds, ...custodianManagersIds]; @@ -2454,7 +2454,7 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.WORKFLOWASSIGNED: // 1. Get managers for publisher - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team.toObject(), 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team.toObject(), constants.roleMemberTeam.CUST_DAR_MANAGER); // 2. Get managerIds for notifications custodianManagersIds = custodianManagers.map(user => user.id); // 3. deconstruct and set options for notifications and email @@ -2592,7 +2592,7 @@ export default class DataRequestController extends Controller { // Custodian notification if (_.has(accessRecord.datasets[0], 'publisher.team.users')) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2602,7 +2602,7 @@ export default class DataRequestController extends Controller { ); } else if (_.has(accessRecord, 'publisherObj')) { // Retrieve all custodian user Ids to generate notifications - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2693,7 +2693,7 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.MESSAGESENT: if (userType === constants.userTypes.APPLICANT) { - const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); const custodianManagersIds = custodianManagers.map(user => user.id); notificationTeam = accessRecord.publisherObj.team.notifications; @@ -2728,7 +2728,7 @@ export default class DataRequestController extends Controller { false ); } else if (userType === constants.userTypes.CUSTODIAN) { - const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleMemberTeam.CUST_DAR_MANAGER); const custodianManagersIds = custodianManagers.map(user => user.id); notificationTeam = accessRecord.publisherObj.team.notifications; From 8dd939c25224b8e857e4871ed3c884e94699060d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 5 Jun 2023 18:32:58 +0100 Subject: [PATCH 224/229] update chart version for send perms --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 7a674d87..2cf6e8a9 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.4 +v5.0.5 From 688df16a136e6163d8aeb50ec0e332d60acbf6d6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 15 Jun 2023 11:42:31 +0100 Subject: [PATCH 225/229] send notifications just for DAR Managers --- Chart.yaml | 2 +- src/resources/message/message.controller.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 2cf6e8a9..1ef06e2b 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.5 +v5.0.6 diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index d5730a78..c210382c 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -131,16 +131,25 @@ module.exports = { [constants.teamNotificationTypes.DATAACCESSREQUEST] ); if (!_.isEmpty(subscribedMembersByType)) { + // build cleaner array of memberIds from subscribedMembersByType if (topicObj.topicMessages !== undefined) { - const memberIds = [...subscribedMembersByType.map(m => m.memberid.toString()), ...topicObj.createdBy._id.toString()]; + const memberIds = [...subscribedMembersByType.map(m => { + if (m.roles.includes('custodian.dar.manager')) { + return m.memberid.toString(); + } + }), ...topicObj.createdBy._id.toString()].filter(elem => elem); // returns array of objects [{email: 'email@email.com '}] for members in subscribed emails users is list of full user object const { memberEmails } = teamController.getMemberDetails([...memberIds], [...messageRecipients]); messageRecipients = [...teamNotificationEmails, ...memberEmails]; } else { - const memberIds = [...subscribedMembersByType.map(m => m.memberid.toString())].filter( + const memberIds = [...subscribedMembersByType.map(m => { + if (m.roles.includes('custodian.dar.manager')) { + return m.memberid.toString(); + } + })].filter( ele => ele !== topicObj.createdBy.toString() - ); + ).filter(elem => elem); const creatorObjectId = topicObj.createdBy.toString(); // returns array of objects [{email: 'email@email.com '}] for members in subscribed emails users is list of full user object const { memberEmails } = teamController.getMemberDetails([...memberIds], [...messageRecipients]); From 0afeacb5abd36b50baaac0a4519ea8ea1ecaef67 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 19 Jun 2023 17:26:10 +0100 Subject: [PATCH 226/229] update topics --- src/resources/topic/topic.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/topic/topic.controller.js b/src/resources/topic/topic.controller.js index 17b61d1f..31bcc67c 100644 --- a/src/resources/topic/topic.controller.js +++ b/src/resources/topic/topic.controller.js @@ -14,7 +14,7 @@ module.exports = { process.stdout.write(`A topic cannot be created with only the creating user\n`); return []; } - let recipients = members.filter(mem => mem.roles.includes('manager') || mem.roles.includes('reviewer')).map(m => m.memberid); + let recipients = members.filter(mem => mem.roles.includes('manager') || mem.roles.includes('custodian.dar.manager')).map(m => m.memberid); // 2. Return team recipients plus the user that created the message recipients = [...recipients, createdBy]; return recipients; From 0294a2b42940fb9c8e44ea37533f4016e91af453 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 19 Jun 2023 17:26:49 +0100 Subject: [PATCH 227/229] update chart --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 1ef06e2b..2774c77a 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.6 +v5.0.7 From 718ff01d15075c63a3ce5e8d87389a8db5c702b3 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 19 Jun 2024 19:04:28 +0100 Subject: [PATCH 228/229] SUPP-1558 :: unable to edit datasets in ALLIANCE>SAIL /datasets --- src/services/datasetonboarding.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index 54580148..6da1f77d 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -209,7 +209,7 @@ export default class DatasetOnboardingService { ], }, }, - ]).exec(); + ]).allowDiskUse(true).exec(); const versionedDatasets = datasets[0].datasets.length > 0 ? datasets[0].datasets : []; From 078f5406c5f221ea850728194e82538c2f8d1741 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 20 Jun 2024 09:44:56 +0100 Subject: [PATCH 229/229] update --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 2774c77a..5025efcc 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.7 +v5.0.8
@@ -2039,7 +2039,7 @@ const _generateMetadataOnboardingSumbitted = options => {
- View datasets pending approval + View datasets pending approval
- View dataset dashboard + View dataset dashboard
- View dataset dashboard + View dataset dashboard
+ User has been removed +
+ You have been removed as a user for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Process applications and communicate with applicants through the Gateway.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
+ User has been removed +
+ You have been removed as a user for the ${publisherName} team on the Gateway. +
+ You can no longer:
+
    +
  • Process applications and communicate with applicants through the Gateway.
  • +
+
+ For more information, please contact a Team Admin for your team:
+ ${teamAdmin} +
- You can no longer:
-
    -
  • Process applications and communicate with applicants through the Gateway.
  • -
-
For more information, please contact a Team Admin for your team:
From ec367f879005a305242750c13c9d7ac9f51e5026 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 1 Jun 2023 12:24:45 +0100 Subject: [PATCH 218/229] update chart to 5.0.2 --- Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Chart.yaml b/Chart.yaml index 4bccc27e..2adff4ca 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.1 +v5.0.2 From c54186d8eb5a4cec2ca96dfa501d970425d8ceb3 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 1 Jun 2023 13:52:39 +0100 Subject: [PATCH 219/229] update subject email delete user from team --- Chart.yaml | 2 +- src/resources/team/v3/util/emailTeam.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 2adff4ca..5f81b6ab 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -1 +1 @@ -v5.0.2 +v5.0.3 diff --git a/src/resources/team/v3/util/emailTeam.js b/src/resources/team/v3/util/emailTeam.js index afd54412..523f4ead 100644 --- a/src/resources/team/v3/util/emailTeam.js +++ b/src/resources/team/v3/util/emailTeam.js @@ -49,7 +49,7 @@ const subjectEmail = (teamName = '', userName = '', role, status, deleteUser) => break; default: if (deleteUser) { - subject = `You have been removed as a user for the ${publisherName} team on the Gateway.`; + subject = `You have been removed as a user from the ${publisherName} team on the Gateway.`; } break; } @@ -415,7 +415,7 @@ const bodyEmail = (teamName = '', currentUserName = '', userName = '', role, sta
- You have been removed as a user for the ${publisherName} team on the Gateway. + You have been removed as a user from the ${publisherName} team on the Gateway.
+ + + Custodian Team Admin has been assigned + +
+ + + Custodian Team Admin has been removed + +
+ + + Metadata Manager has been assigned + +
+ + + Metadata Manager has been removed + +
+ + + Metadata Editor has been assigned + +
+ + + Metadata Editor has been removed + +
+ + + DAR Manager has been assigned + +
+ + + DAR Manager has been removed + +
+ + + DAR Reviewer has been assigned + +
+ + + DAR Reviewer has been removed + +
+ + + User has been removed + +