From a694495158e4afdbb11eed5e237caa7428337788 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Thu, 18 Nov 2021 13:13:14 +0000 Subject: [PATCH 001/389] Create pattern repository files for question bank --- src/config/server.js | 2 + src/resources/questionbank/dependency.js | 5 ++ .../questionbank/questionbank.controller.js | 68 +++++++++++++++++++ .../questionbank/questionbank.entity.js | 8 +++ .../questionbank/questionbank.model.js | 19 ++++++ .../questionbank/questionbank.repository.js | 19 ++++++ .../questionbank/questionbank.route.js | 40 +++++++++++ .../questionbank/questionbank.service.js | 17 +++++ 8 files changed, 178 insertions(+) create mode 100644 src/resources/questionbank/dependency.js create mode 100644 src/resources/questionbank/questionbank.controller.js create mode 100644 src/resources/questionbank/questionbank.entity.js create mode 100644 src/resources/questionbank/questionbank.model.js create mode 100644 src/resources/questionbank/questionbank.repository.js create mode 100644 src/resources/questionbank/questionbank.route.js create mode 100644 src/resources/questionbank/questionbank.service.js diff --git a/src/config/server.js b/src/config/server.js index 3fc08979..53dce783 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -254,6 +254,8 @@ app.use('/api/v1/global', require('../resources/global/global.route')); app.use('/api/v1/search-preferences', require('../resources/searchpreferences/searchpreferences.route')); +app.use('/api/v2/questionbank', require('../resources/questionbank/questionbank.route')); + initialiseAuthentication(app); // launch our backend into a port diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js new file mode 100644 index 00000000..9843acd7 --- /dev/null +++ b/src/resources/questionbank/dependency.js @@ -0,0 +1,5 @@ +import QuestionbankRepository from './questionbank.repository'; +import QuestionbankService from './questionbank.service'; + +export const questionbankRepository = new QuestionbankRepository(); +export const questionbankService = new QuestionbankService(questionbankRepository); diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js new file mode 100644 index 00000000..23f7a8ed --- /dev/null +++ b/src/resources/questionbank/questionbank.controller.js @@ -0,0 +1,68 @@ +import Controller from '../base/controller'; +import { logger } from '../utilities/logger'; + +const logCategory = 'questionbank'; + +export default class QuestionbankController extends Controller { + constructor(questionbankService) { + super(questionbankService); + this.questionbankService = questionbankService; + } + + async getQuestionbank(req, res) { + try { + // Extract id parameter from query string + const { id } = req.params; + // If no id provided, it is a bad request + if (!id) { + return res.status(400).json({ + success: false, + message: 'You must provide a questionbank identifier', + }); + } + // Find the questionbank + const options = { lean: true }; + const questionbank = await this.questionbankService.getQuestionbank(id, req.query, options); + // Return if no questionbank found + if (!questionbank) { + return res.status(404).json({ + success: false, + message: 'A questionbank could not be found with the provided id', + }); + } + // Return the questionbank + return res.status(200).json({ + success: true, + ...questionbank + }); + } 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', + }); + } + } + + async getQuestionbanks(req, res) { + try { + // Find the relevant questionbanks + const questionbanks = await this.questionbankService.getQuestionbanks(req.query).catch(err => { + logger.logError(err, logCategory); + }); + // Return the questionbanks + return res.status(200).json({ + success: true, + data: questionbanks, + }); + } 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', + }); + } + } +} diff --git a/src/resources/questionbank/questionbank.entity.js b/src/resources/questionbank/questionbank.entity.js new file mode 100644 index 00000000..9836a64b --- /dev/null +++ b/src/resources/questionbank/questionbank.entity.js @@ -0,0 +1,8 @@ +import Entity from '../base/entity'; + +export default class QuestionbankClass extends Entity { + constructor(obj) { + super(); + Object.assign(this, obj); + } +} diff --git a/src/resources/questionbank/questionbank.model.js b/src/resources/questionbank/questionbank.model.js new file mode 100644 index 00000000..244e91d5 --- /dev/null +++ b/src/resources/questionbank/questionbank.model.js @@ -0,0 +1,19 @@ +import { model, Schema } from 'mongoose'; + +import QuestionbankClass from './questionbank.entity'; + +const questionbankSchema = new Schema( + { + // New object properties here + }, + { + timestamps: true, + toJSON: { virtuals: true }, + toObject: { virtuals: true }, + } +); + +// Load entity class +questionbankSchema.loadClass(QuestionbankClass); + +export const Questionbank = model('Questionbank', questionbankSchema); \ No newline at end of file diff --git a/src/resources/questionbank/questionbank.repository.js b/src/resources/questionbank/questionbank.repository.js new file mode 100644 index 00000000..4558938f --- /dev/null +++ b/src/resources/questionbank/questionbank.repository.js @@ -0,0 +1,19 @@ +import Repository from '../base/repository'; +import { Questionbank } from './questionbank.model'; +import constants from '../utilities/constants.util'; + +export default class QuestionbankRepository extends Repository { + constructor() { + super(Questionbank); + this.questionbank = Questionbank; + } + + async getQuestionbank(query, options) { + return this.findOne(query, options); + } + + async getQuestionbanks(query) { + const options = { lean: true }; + return this.find(query, options); + } +} diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js new file mode 100644 index 00000000..c9508d7d --- /dev/null +++ b/src/resources/questionbank/questionbank.route.js @@ -0,0 +1,40 @@ +import express from 'express'; +import passport from 'passport'; +import { isUndefined } from 'lodash'; +import QuestionbankController from './questionbank.controller'; +import { questionbankService } from './dependency'; +import { logger } from '../utilities/logger'; + +const router = express.Router(); +const questionbankController = new QuestionbankController(questionbankService); +const logCategory = 'questionbank'; + +const validateViewRequest = (req, res, next) => { + const { teamId } = req.params; + if (isUndefined(teamId)) return res.status(400).json({ success: false, message: 'You must provide a valid teamId' }); + + next(); +}; + +const authorizeView = (req, res, next) => {}; + +// @route GET /api/v1/questionbanks/teamId +// @desc Returns a questionbank based on ID of the publisher provided +// @access Public +router.get( + '/:teamId', + passport.authenticate('jwt'), + validateViewRequest, + authorizeView, + logger.logRequestMiddleware({ logCategory, action: 'Viewed questionbank data' }), + (req, res) => questionbankController.getQuestionbank(req, res) +); + +// @route GET /api/v1/questionbanks +// @desc Returns a collection of questionbanks based on supplied query parameters +// @access Public +router.get('/', logger.logRequestMiddleware({ logCategory, action: 'Viewed questionbanks data' }), (req, res) => + questionbankController.getQuestionbanks(req, res) +); + +module.exports = router; diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js new file mode 100644 index 00000000..c39a0b0a --- /dev/null +++ b/src/resources/questionbank/questionbank.service.js @@ -0,0 +1,17 @@ +export default class QuestionbankService { + constructor(questionbankRepository) { + this.questionbankRepository = questionbankRepository; + } + + getQuestionbank(id, query = {}, options = {}) { + // Protect for no id passed + if (!id) return; + + query = { ...query, id }; + return this.questionbankRepository.getQuestionbank(query, options); + } + + getQuestionbanks(query = {}) { + return this.questionbankRepository.getQuestionbanks(query); + } +} From bed06d1ba5c8f45f96f53c79c4aa1129c8b8f95a Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Mon, 22 Nov 2021 17:24:58 +0000 Subject: [PATCH 002/389] Add first version of GET question bank API --- .../schema/datarequest.schemas.model.js | 76 +- src/resources/questionbank/dependency.js | 4 +- src/resources/questionbank/masterSchema.json | 2330 +++++++++++++++++ .../questionbank/questionbank.controller.js | 36 +- .../questionbank/questionbank.route.js | 34 +- .../questionbank/questionbank.service.js | 139 +- 6 files changed, 2552 insertions(+), 67 deletions(-) create mode 100644 src/resources/questionbank/masterSchema.json diff --git a/src/resources/datarequest/schema/datarequest.schemas.model.js b/src/resources/datarequest/schema/datarequest.schemas.model.js index d1a5eab7..01bc0d50 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.model.js +++ b/src/resources/datarequest/schema/datarequest.schemas.model.js @@ -2,36 +2,48 @@ import { model, Schema } from 'mongoose'; import constants from '../../utilities/constants.util'; -const DataRequestSchemas = new Schema({ - id: Number, - status: String, - version: { - type: Number, - default: 1 - }, - dataSetId: { - type: String, - default: '', - }, - publisher: { - type: String, - default: '' - }, - formType: { - type: String, - default: constants.formTypes.Extended5Safe, - enum: Object.values(constants.formTypes) - }, - jsonSchema: { - type: Object, - default: {} - }, - isCloneable: Boolean -}, { - timestamps: true -}); - - -export const DataRequestSchemaModel = model('data_request_schemas', DataRequestSchemas); - +const DataRequestSchemas = new Schema( + { + id: Number, + status: String, + version: { + type: Number, + default: 1, + }, + dataSetId: { + type: String, + default: '', + }, + publisher: { + type: String, + default: '', + }, + formType: { + type: String, + default: constants.formTypes.Extended5Safe, + enum: Object.values(constants.formTypes), + }, + jsonSchema: { + type: Object, + default: {}, + }, + isCloneable: Boolean, + guidance: { + type: Object, + default: {}, + }, + questionStatus: { + type: Object, + default: {}, + }, + countOfChanges: { + type: Number, + default: 0, + }, + }, + { + timestamps: true, + } +); +export const DataRequestSchemaModel = model('data_request_schemas', DataRequestSchemas); diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js index 9843acd7..49b21029 100644 --- a/src/resources/questionbank/dependency.js +++ b/src/resources/questionbank/dependency.js @@ -1,5 +1,7 @@ import QuestionbankRepository from './questionbank.repository'; import QuestionbankService from './questionbank.service'; +import { publisherService } from '../publisher/dependency'; +import { globalService } from '../global/dependency'; export const questionbankRepository = new QuestionbankRepository(); -export const questionbankService = new QuestionbankService(questionbankRepository); +export const questionbankService = new QuestionbankService(questionbankRepository, publisherService, globalService); diff --git a/src/resources/questionbank/masterSchema.json b/src/resources/questionbank/masterSchema.json new file mode 100644 index 00000000..f637b004 --- /dev/null +++ b/src/resources/questionbank/masterSchema.json @@ -0,0 +1,2330 @@ +{ + "_id": { "$oid": "615f1c4be27d54d9fdc494fb" }, + "localeId": "en-gb", + "__v": { "$numberInt": "0" }, + "createdAt": { "$date": { "$numberLong": "1633623114800" } }, + "entry": { + "name": "dataUtility", + "items": [ + { + "key": "allowable_uses", + "dimension": "Allowable uses", + "category": "Access & Provision", + "definition": "Allowable dataset usages as per the licencing agreement, following ethical and IG approval", + "includeInWizard": true, + "wizardStepTitle": "Allowable uses", + "wizardStepDescription": "Please select the identifier that is most relevant to you (optional)", + "wizardStepOrder": { "$numberInt": "1" }, + "wizardStepType": "radio", + "entries": [ + { + "id": { "$oid": "615f1c4a70e33243450fdf6a" }, + "displayOrder": { "$numberInt": "1" }, + "label": "Academic research", + "impliedValues": ["platinum", "gold", "silver", "bronze"] + }, + { + "id": { "$oid": "615f1c4a70e3320a980fdf6b" }, + "displayOrder": { "$numberInt": "2" }, + "label": "Student / educational project", + "impliedValues": ["platinum", "silver", "gold", "bronze"] + }, + { + "id": { "$oid": "615f1c4a70e332c6150fdf6c" }, + "displayOrder": { "$numberInt": "3" }, + "label": "Charitable or non-profit project", + "impliedValues": ["platinum", "gold", "silver"] + }, + { + "id": { "$oid": "615f1c4a70e33284ae0fdf6d" }, + "displayOrder": { "$numberInt": "4" }, + "label": "Commercial project", + "impliedValues": ["platinum", "gold"] + } + ] + }, + { + "key": "time_lag", + "dimension": "Time Lag", + "category": "Access & Provision", + "definition": "Lag between the data being collected and added to the dataset", + "includeInWizard": true, + "wizardStepTitle": "Time lag", + "wizardStepDescription": "What is the maximum acceptable time delay from the data being generated to entering the dataset? (optional)", + "wizardStepOrder": { "$numberInt": "2" }, + "wizardStepType": "radio", + "entries": [ + { + "id": { "$oid": "615f1c4a70e3326b190fdf6e" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Effectively real-time data", + "label": "Near real-time data", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332a2c60fdf6f" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Approximately 1 week", + "label": "1 week", + "impliedValues": ["platinum", "gold"] + }, + { + "id": { "$oid": "615f1c4a70e33284c10fdf70" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Approximately 1 month", + "label": "1 month", + "impliedValues": ["platinum", "gold", "silver"] + }, + { + "id": { "$oid": "615f1c4a70e332ed880fdf71" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Approximately 1 year", + "label": "1 year", + "impliedValues": ["platinum", "gold", "silver", "bronze"] + } + ] + }, + { + "key": "timeliness", + "dimension": "Timeliness", + "category": "Access & Provision", + "definition": "Average data access request timeframe", + "entries": [ + { + "id": { "$oid": "615f1c4a70e332284b0fdf72" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Less than 2 weeks", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332f1080fdf73" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Less than 1 month", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332c9210fdf74" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Less than 3 months", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33242ec0fdf75" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Less than 6 months", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + }, + { + "key": "data_quality_management_process", + "dimension": "Data Quality Management Process", + "category": "Technical Quality", + "definition": "The level of maturity of the data quality management processes ", + "entries": [ + { + "id": { "$oid": "615f1c4a70e332ce2f0fdf76" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Externally verified compliance with the data management plan, e.g. by ISO, CQC, ICO or other body", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332d3810fdf77" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Evidence that the data management plan has been implemented is available", + "impliedValues": ["silver", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33261b60fdf78" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "A documented data management plan covering collection, auditing, and management is available for the dataset", + "impliedValues": ["bronze", "silver", "platinum"] + } + ] + }, + { + "key": "pathway_coverage", + "dimension": "Pathway coverage", + "category": "Coverage", + "definition": "Representation of multi-disciplinary healthcare data", + "entries": [ + { + "id": { "$oid": "615f1c4a70e33200ba0fdf79" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Contains data across more than two tiers", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e3320f2b0fdf7a" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Contains multimodal data or data that is linked across two tiers (e.g. primary and secondary care)", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33277bc0fdf7b" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Contains data from multiple specialties or services within a single tier of care", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33236b50fdf7c" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Contains data from a single speciality or area", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + }, + { + "key": "length_of_follow_up", + "dimension": "Length of follow up", + "category": "Coverage", + "definition": "Average timeframe in which a patient appears in a dataset (follow up period)", + "wizardStepTitle": "Length of follow up", + "wizardStepDescription": "What is the minimum required time frame for patients appearing in dataset, on average? (optional)", + "includeInWizard": true, + "wizardStepOrder": { "$numberInt": "3" }, + "wizardStepType": "radio", + "entries": [ + { + "id": { "$oid": "615f1c4a70e3327fe00fdf7d" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Between 1-6 months", + "label": "Between 1-6 months", + "impliedValues": ["platinum", "gold", "silver", "bronze"] + }, + { + "id": { "$oid": "615f1c4a70e33218480fdf7e" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Between 6-12 months", + "label": "Between 6-12 months", + "impliedValues": ["platinum", "gold", "silver"] + }, + { + "id": { "$oid": "615f1c4a70e33217000fdf7f" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Between 1-10 years", + "label": "Between 1-10 years", + "impliedValues": ["platinum", "gold"] + }, + { + "id": { "$oid": "615f1c4a70e332fb220fdf80" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "More than 10 years", + "label": "More than 10 years", + "impliedValues": ["platinum"] + } + ] + }, + { + "key": "availability_of_additional_documentation_and_support", + "dimension": "Availability of additional documentation and support", + "category": "Data Documentation", + "definition": "Available dataset documentation in addition to the data dictionary", + "entries": [ + { + "id": { "$oid": "615f1c4a70e3326e4a0fdf81" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "As Gold, plus support personnel available to answer questions", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33230810fdf82" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "As Silver, plus dataset publication was supported with a journal article explaining the dataset in detail, or dataset training materials", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33231900fdf83" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Comprehensive ReadMe describing extracting and use of data, Dataset FAQS available, Visual data model provided", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33206280fdf84" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Past journal articles demonstrate that knowledge of the data exists", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + }, + { + "key": "data_model", + "dimension": "Data Model", + "category": "Data Documentation", + "definition": "Availability of clear, documented data model", + "includeInWizard": true, + "wizardStepTitle": "Data model", + "wizardStepDescription": "What data model requirements do you have? (optional)", + "wizardStepOrder": { "$numberInt": "4" }, + "wizardStepType": "radio", + "entries": [ + { + "id": { "$oid": "615f1c4a70e332cd770fdf85" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Known and accepted data model but some key field un-coded or free text", + "label": "Known and accepted data model, with some key fields uncoded ", + "impliedValues": ["platinum", "gold", "silver", "bronze"] + }, + { + "id": { "$oid": "615f1c4a70e3326ff80fdf86" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Key fields codified using a local standard", + "label": "Key fields coded using local standard", + "impliedValues": ["platinum", "gold", "silver"] + }, + { + "id": { "$oid": "615f1c4a70e332dffc0fdf87" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Key fields codified using a national or international standard", + "label": "Key fields coded using national/international standard", + "impliedValues": ["platinum", "gold"] + }, + { + "id": { "$oid": "615f1c4a70e33265420fdf88" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Data Model conforms to a national standard and key fields codified using a national/international standard", + "label": "Model conforms to national standard and key fields coded to national/internal standard", + "impliedValues": ["platinum"] + } + ] + }, + { + "key": "data_dictionary", + "dimension": "Data Dictionary", + "category": "Data Documentation", + "definition": "Provided documented data dictionary and terminologies", + "entries": [ + { + "id": { "$oid": "615f1c4a70e3327e410fdf89" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Dictionary is based on international standards and includes mapping", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332c0710fdf8a" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Dictionary relates to national definitions", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332cf5c0fdf8b" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Definitions compiled into local data dictionary which is available online", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33286440fdf8c" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Data definitions available", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + }, + { + "key": "provenance", + "dimension": "Provenance", + "category": "Data Documentation", + "definition": "Clear description of source and history of the dataset, providing a \"transparent data pipeline\"", + "includeInWizard": true, + "wizardStepTitle": "Provenance", + "wizardStepDescription": "To what level of detail do you require the origin of the dataset to be documented? (optional)", + "wizardStepOrder": { "$numberInt": "5" }, + "wizardStepType": "radio", + "entries": [ + { + "id": { "$oid": "615f1c4a70e33250010fdf8d" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Source of the dataset is documented", + "label": "Dataset source documented", + "impliedValues": ["platinum", "gold", "silver", "bronze"] + }, + { + "id": { "$oid": "615f1c4a70e33244eb0fdf8e" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "Source of the dataset and any transformations, rules and exclusions documented", + "label": "Dataset source, any transformations, rule and exclusions documented", + "impliedValues": ["platinum", "gold", "silver"] + }, + { + "id": { "$oid": "615f1c4a70e33229a80fdf8f" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "All original data items listed, all transformations, rules and exclusion listed and impact of these", + "label": "All original data items, transformations, rules, exclusions and impact listed", + "impliedValues": ["platinum", "gold"] + }, + { + "id": { "$oid": "615f1c4a70e33231780fdf90" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Ability to view earlier versions, including versions before any transformations have been applied data (in line with deidentification and IG approval) and review the impact of each stage of data cleaning", + "label": "Earlier and 'raw' versions and the impact of each stage of data cleaning", + "impliedValues": ["platinum"] + } + ] + }, + { + "includeInWizard": true, + "wizardStepTitle": "Search terms", + "wizardStepDescription": "Please type in any relevant search terms to refine your search (optional)", + "wizardStepOrder": { "$numberInt": "6" }, + "wizardStepType": "search", + "entries": [] + }, + { + "key": "linkages", + "dimension": "Linkages", + "category": "Value & Interest", + "definition": "Ability to link with other datasets", + "entries": [ + { + "id": { "$oid": "615f1c4a70e332ffaf0fdf91" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "Existing linkage with reusable or downstream approvals", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e3323aca0fdf92" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "List of restrictions on the type of linkages detailed. List of previously successful dataset linkages performed, with navigable links to linked datasets via at DOI/URL", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33237280fdf93" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "Available linkages outlined and/or List of datasets previously successfully linked provided", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332e92d0fdf94" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "Identifiers to demonstrate ability to link to other datasets", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + }, + { + "key": "data_enrichments", + "dimension": "Data Enrichments", + "category": "Value & Interest", + "definition": "Data sources enriched with annotations, image labels, phenomes, derivations, NLP derived data labels", + "entries": [ + { + "id": { "$oid": "615f1c4a70e33271010fdf95" }, + "displayOrder": { "$numberInt": "1" }, + "definition": "The data include additional derived fields, or enriched data", + "impliedValues": ["platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33264900fdf96" }, + "displayOrder": { "$numberInt": "2" }, + "definition": "The data include additional derived fields, or enriched data used by other available data sources", + "impliedValues": ["gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e33233050fdf97" }, + "displayOrder": { "$numberInt": "3" }, + "definition": "The derived fields or enriched data were generated from, or used by, a peer reviewed algorithm", + "impliedValues": ["silver", "gold", "platinum"] + }, + { + "id": { "$oid": "615f1c4a70e332ea210fdf98" }, + "displayOrder": { "$numberInt": "4" }, + "definition": "The data includes derived fields or enriched data from a national report", + "impliedValues": ["bronze", "silver", "gold", "platinum"] + } + ] + } + ] + }, + "languageCode": "en", + "updatedAt": { "$date": { "$numberLong": "1633623114800" } }, + "masterSchema": { + "pages": [ + { + "active": true, + "description": "Who is going to be accessing the data?\n\nSafe People should have the right motivations for accessing research data and understand the legal and ethical considerations when using data that may be sensitive or confidential. Safe People should also have sufficient skills, knowledge and experience to work with the data effectively. Researchers may need to undergo specific training or accreditation before accessing certain data or research environments and demonstrate that they are part of a bona fide research organisation.\n\nThe purpose of this section is to ensure that:\n- details of people who will be accessing the data and the people who are responsible for completing the application are identified\n- any individual or organisation that intends to access the data requested is identified\n- all identified individuals have the necessary accreditation and/or expertise to work with the data effectively.", + "title": "Safe people", + "pageId": "safepeople" + }, + { + "pageId": "safeproject", + "description": "What is the purpose of accessing the data?\n\nSafe projects are those that have a valid research purpose with a defined public benefit. \nFor access to data to be granted the researchers need to demonstrate that their proposal is an appropriate and ethical use of the data, and that it is intended to deliver clear public benefits. The purpose of this section is to ensure that:\n- the project rationale is explained in lay terms\n- the research purpose has a defined public benefit. This can be new knowledge, new treatments, improved pathways of care, new techniques of training staff. \n- how the data requested will be used to achieve the project objectives is articulated.", + "active": false, + "title": "Safe project" + }, + { + "pageId": "safedata", + "description": "Safe data ensure that researchers have a clear legal basis for accessing the data and do not inadvertently learn something about the data subjects during the course of their analysis, minimising the risks of re-identification.\nThe minimisation of this risk could be achieved by removing direct identifiers, aggregating values, banding variables, or other statistical techniques that may make re-identification more difficult. Sensitive or confidential data could not be considered to be completely safe because of the residual risk to a data subject’s confidentiality. Hence other limitations on access will need to be applied.\n\nThe purpose of this section is to ensure that: \n- there is a clear legal basis for accessing the requested data\n- the data requested is proportionate to the requirement of the project \n- all data requested is necessary in order to achieve the public benefit declared \n- data subjects cannot be identified by your team by cross-referencing datasets from anywhere else.", + "active": false, + "title": "Safe data" + }, + { + "active": false, + "description": "Safe settings are analytics environments where researchers can access and analyse the requested datasets in a safe and ethical way. Safe settings encompass the physical environment and procedural arrangements such as the supervision and auditing regimes. For safe settings, the likelihood of both deliberate and accidental disclosure needs to be explicitly considered.\n\nThe purpose of this section is to ensure that:\n\n- researchers access requested data in a secure and controlled setting such as a Trusted Research Environment (TRE) that limits the unauthorised use of the data\n- practical controls and appropriate restrictions are in place if researchers access data though non-TRE environment. There may be requirements that data is held on restricted access servers, encrypted and only decrypted at the point of use.", + "title": "Safe settings", + "pageId": "safesettings" + }, + { + "pageId": "safeoutputs", + "description": "Safe outputs ensure that all research outputs cannot be used to identify data subjects. They typically include ‘descriptive statistics’ that have been sufficiently aggregated such that identification is near enough impossible, and modelled outputs which are inherently non-confidential. The purpose of this section is to ensure that:\n\n- controls are in place to minimise risks associated with planned outputs and publications \n- the researchers aim to openly publish their results to enable use, scrutiny and further research.", + "active": false, + "title": "Safe outputs" + } + ], + "formPanels": [ + { "pageId": "safepeople", "index": { "$numberDouble": "1.0" }, "panelId": "primaryapplicant" }, + { "index": { "$numberDouble": "2.0" }, "pageId": "safepeople", "panelId": "safepeople-otherindividuals" }, + { "index": { "$numberDouble": "3.0" }, "pageId": "safeproject", "panelId": "safeproject-aboutthisapplication" }, + { "pageId": "safeproject", "index": { "$numberDouble": "4.0" }, "panelId": "safeproject-projectdetails" }, + { "panelId": "safeproject-funderinformation", "index": { "$numberDouble": "5.0" }, "pageId": "safeproject" }, + { "panelId": "safeproject-sponsorinformation", "pageId": "safeproject", "index": { "$numberDouble": "6.0" } }, + { "panelId": "safeproject-declarationofinterest", "index": { "$numberDouble": "7.0" }, "pageId": "safeproject" }, + { "pageId": "safeproject", "index": { "$numberDouble": "8.0" }, "panelId": "safeproject-intellectualproperty" }, + { "panelId": "safedata-datafields", "pageId": "safedata", "index": { "$numberDouble": "9.0" } }, + { "pageId": "safedata", "index": { "$numberDouble": "10.0" }, "panelId": "safedata-otherdatasetsintentiontolinkdata" }, + { "panelId": "safedata-lawfulbasis", "index": { "$numberDouble": "11.0" }, "pageId": "safedata" }, + { "pageId": "safedata", "index": { "$numberDouble": "12.0" }, "panelId": "safedata-confidentialityavenue" }, + { "pageId": "safedata", "index": { "$numberDouble": "13.0" }, "panelId": "safedata-ethicalapproval" }, + { "index": { "$numberDouble": "14.0" }, "pageId": "safesettings", "panelId": "safesettings-storageandprocessing" }, + { "pageId": "safesettings", "index": { "$numberDouble": "15.0" }, "panelId": "safesettings-dataflow" }, + { "index": { "$numberDouble": "16.0" }, "pageId": "safeoutputs", "panelId": "safeoutputs-outputsdisseminationplans" }, + { "pageId": "safeoutputs", "index": { "$numberDouble": "17.0" }, "panelId": "safeoutputs-retention" } + ], + "questionPanels": [ + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safepeople", + "navHeader": "Primary applicant", + "panelId": "primaryapplicant", + "panelHeader": "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", + "questionSets": [{ "questionSetId": "primaryapplicant", "index": { "$numberDouble": "1.0" } }] + }, + { + "navHeader": "Other individuals", + "panelHeader": "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", + "panelId": "safepeople-otherindividuals", + "questionSets": [ + { "questionSetId": "safepeople-otherindividuals", "index": { "$numberDouble": "1.0" } }, + { "index": { "$numberDouble": "100.0" }, "questionSetId": "add-safepeople-otherindividuals" } + ], + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safepeople" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safeproject", + "navHeader": "About this application", + "panelHeader": "", + "panelId": "safeproject-aboutthisapplication", + "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-aboutthisapplication" }] + }, + { + "questionSets": [{ "questionSetId": "safeproject-projectdetails", "index": { "$numberDouble": "1.0" } }], + "panelHeader": "", + "panelId": "safeproject-projectdetails", + "navHeader": "Project details", + "pageId": "safeproject", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-funderinformation" }], + "panelHeader": "A funder is the organisation or body providing the financial resource to make the project possible, and may be different to the organisation detailed in the Safe people section. Please provide details of the main funder organisations supporting this project.\n\nPlease use the file upload function if you're not able to add all funders via the form.", + "panelId": "safeproject-funderinformation", + "navHeader": "Funder information", + "pageId": "safeproject", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "navHeader": "Sponsor information", + "questionSets": [{ "questionSetId": "safeproject-sponsorinformation", "index": { "$numberDouble": "1.0" } }], + "panelId": "safeproject-sponsorinformation", + "panelHeader": "Please use the file upload function if you're not able to add all sponsors via the form.", + "pageId": "safeproject", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "panelHeader": "", + "panelId": "safeproject-declarationofinterest", + "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-declarationofinterest" }], + "navHeader": "Declaration of interest", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safeproject" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safeproject", + "navHeader": "Intellectual property", + "panelId": "safeproject-intellectualproperty", + "panelHeader": "", + "questionSets": [{ "questionSetId": "safeproject-intellectualproperty", "index": { "$numberDouble": "1.0" } }] + }, + { + "questionSets": [{ "questionSetId": "safedata-datafields", "index": { "$numberDouble": "1.0" } }], + "panelHeader": "", + "panelId": "safedata-datafields", + "navHeader": "Data fields", + "pageId": "safedata", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "questionSets": [{ "questionSetId": "safedata-otherdatasetsintentiontolinkdata", "index": { "$numberDouble": "1.0" } }], + "panelHeader": "", + "panelId": "safedata-otherdatasetsintentiontolinkdata", + "navHeader": "Other datasets - Intention to link data", + "pageId": "safedata", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "navHeader": "Lawful basis", + "panelId": "safedata-lawfulbasis", + "panelHeader": "General Data Protection Regulation (GDPR) applies to ‘controllers’ and ‘processors’. \n\nA controller determines the purposes and means of processing personal data.\n\nA processor is responsible for processing personal data on behalf of a controller.\n \nGDPR applies to processing carried out by organisations operating within the EU. It also applies to organisations outside the EU that offer goods or services to individuals in the EU.\nGDPR does not apply to certain activities including processing covered by the Law Enforcement Directive, processing for national security purposes and processing carried out by individuals purely for personal/household activities. \n \nGDPR only applies to information which relates to an identifiable living individual. Information relating to a deceased person does not constitute personal data and therefore is not subject to the GDPR.", + "questionSets": [{ "questionSetId": "safedata-lawfulbasis", "index": { "$numberDouble": "1.0" } }], + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safedata" + }, + { + "pageId": "safedata", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "navHeader": "Confidentiality avenue", + "questionSets": [{ "questionSetId": "safedata-confidentialityavenue", "index": { "$numberDouble": "1.0" } }], + "panelHeader": "", + "panelId": "safedata-confidentialityavenue" + }, + { + "pageId": "safedata", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safedata-ethicalapproval" }], + "panelId": "safedata-ethicalapproval", + "panelHeader": "This section details the research and ethics approval which you have obtained or sought for your project, or otherwise provides evidence as to why such approval is not necessary. For instance, for data analysis done on data deemed anonymous, no ethical approval is explicitly needed for said analyses. \n\nWhere such approval is not in place, it is important that you demonstrate why this is the case and provide assurances if approval is pending. If you need advice on whether ethics approval is necessary, you should approach your local ethics services in the first instance. Information about UK research ethics committees and ethical opinions can be found on the Health Research Authority (HRA) website.", + "navHeader": "Ethical approval" + }, + { + "questionSets": [{ "questionSetId": "safesettings-storageandprocessing", "index": { "$numberDouble": "1.0" } }], + "panelHeader": "This section details in what way the proposal aims to store and use data, and controls in place to minimise risks associated with this storage and use. If you have indicated that your proposal seeks to store and use data exclusively through a recognised trusted research environment, then you do not need to complete this section.\n In relation to personal data, means any operation or set of operations which is performed on personal data or on sets of personal data (whether or not by automated means, such as collection, recording, organisation, structuring, storage, alteration, retrieval, consultation, use, disclosure, dissemination, restriction, erasure or destruction).\n \n All Locations where processing will be undertaken, for the avoidance of doubt storage is considered processing. For each separate organisation processing data which is not fully anonymous a separate partner organisation form must also be completed.\n \n Processing, in relation to information or data means obtaining, recording or holding the information or data or carrying out any operation or set of operations on the information or data, including—\n a) organisation, adaptation or alteration of the information or data,\n b) retrieval, consultation or use of the information or data,\n c) disclosure of the information or data by transmission,\n dissemination or otherwise making available, or\n d) alignment, combination, blocking, erasure or destruction of the information or data.\n\nPlease note that where an applicant has explicit consent from participants (such as in cohort studies) to hold linked health records, and therefore request extraction of data held by SAIL back to their own research setting, rather than be accessed solely in the remote desktop environment SAIL providers, a user may apply in these circumstances to SAIL for such extraction, with justification provided through the means of their consent documentation and ethical approval to hold such data outside of our secure setting.\n\nPlease use the file upload function if you're not able to add all organisations via the form.", + "panelId": "safesettings-storageandprocessing", + "navHeader": "Storage and processing", + "pageId": "safesettings", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safesettings", + "panelHeader": "", + "panelId": "safesettings-dataflow", + "questionSets": [{ "questionSetId": "safesettings-dataflow", "index": { "$numberDouble": "1.0" } }], + "navHeader": "Dataflow" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safeoutputs", + "panelId": "safeoutputs-outputsdisseminationplans", + "panelHeader": "", + "questionSets": [{ "questionSetId": "safeoutputs-outputsdisseminationplans", "index": { "$numberDouble": "1.0" } }], + "navHeader": "Outputs dissemination plans" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safeoutputs", + "panelHeader": "", + "panelId": "safeoutputs-retention", + "questionSets": [{ "questionSetId": "safeoutputs-retention", "index": { "$numberDouble": "1.0" } }], + "navHeader": "Retention" + } + ], + "questionSets": [ + { + "questionSetId": "primaryapplicant", + "questionSetHeader": "Primary applicant", + "questions": [ + { + "guidance": "Please insert your full name.", + "validations": [{ "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], + "questionId": "safepeopleprimaryapplicantfullname", + "input": { "type": "textInput", "required": true }, + "question": "Full name", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Job title", + "questionId": "safepeopleprimaryapplicantjobtitle", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], + "input": { "required": true, "type": "textInput" }, + "lockedQuestion": 1, + "defaultQuestion": 1 + }, + { + "questionId": "safepeopleprimaryapplicanttelephone", + "input": { "type": "textInput" }, + "question": "Telephone", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "ORCID", + "input": { "type": "textInput" }, + "questionId": "safepeopleprimaryapplicantorcid", + "guidance": "ORCID provides a persistent digital identifier (an ORCID iD) that you own and control, and that distinguishes you from every other researcher. You can create an ORCID profile at https://orcid.org/. If you have an ORCID iD please include it here. ", + "lockedQuestion": 0, + "defaultQuestion": 1 + }, + { + "question": "Email", + "validations": [ + { "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }, + { "type": "isEmail" } + ], + "input": { "required": true, "type": "textInput" }, + "questionId": "safepeopleprimaryapplicantemail", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { + "options": [ + { "value": "Yes", "text": "Yes" }, + { "value": "No", "text": "No" } + ], + "label": "Will you access the data requested?", + "type": "radioOptionsInput" + }, + "questionId": "safepeopleprimaryapplicantaccessdata", + "guidance": "Please confirm whether the applicant will be accessing the data that is being requested.", + "question": "Will you access the data requested?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Are you an accredited researcher under the Digital Economy Act 2017?", + "input": { + "label": "Are you an accredited researcher under the Digital Economy Act 2017?", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "question": "If yes, please provide your accredited researcher number.", + "input": { "type": "textareaInput" }, + "questionId": "safepeopleprimaryapplicantaccreditedresearchernumber" + } + ] + }, + { + "value": "No", + "text": "No", + "conditionalQuestions": [ + { + "questionId": "safepeopleprimaryapplicantaccreditedresearcherintention", + "input": { "type": "textareaInput" }, + "question": "If no, please specify if you are planning to become an accredited researcher." + } + ] + } + ] + }, + "questionId": "safepeopleprimaryapplicantaccreditedresearcher", + "guidance": "Depending on the type of data you are requesting, you might be required to become an accredited researcher. Most access to data in the Secure Research Service (SRS) will be by researchers accredited under the Digital Economy Act 2017 (DEA). \n\nThe UK Statistics Authority has published further information on the criteria to be met in a Research Code of Practice and Accreditation criteria. Researchers can apply for accreditation through the Research Accreditation Service (RAS).\n\nFull accredited researcher status is valid for five years. Provisional accredited researcher status is valid for one year.\n\nMore information here: https://www.gov.uk/government/publications/digital-economy-act-2017-part-5-codes-of-practice/research-code-of-practice-and-accreditation-criteria#section-b-accreditation-of-researchers-and-peer-reviewers", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Have you undertaken professional training or education on the topic of Information Governance?", + "questionId": "safepeopleprimaryapplicanttraininginformationgovernance", + "input": { + "options": [ + { + "conditionalQuestions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safepeopleprimaryapplicanttraininginformationgovernancerecent", + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", + "question": "Please provide full details regarding the most recent training" + } + ], + "text": "Yes", + "value": "Yes" + }, + { + "text": "No", + "value": "No", + "conditionalQuestions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safepeopleprimaryapplicanttraininginformationgovernanceintention", + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", + "question": "Please provide any details of plans to attend training, if applicable" + } + ] + } + ], + "label": "Have you undertaken professional training or education on the topic of Information Governance?", + "type": "radioOptionsInput" + }, + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safepeopleprimaryapplicantorganisationname", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], + "input": { "type": "textInput", "required": true }, + "guidance": "Please give the full name of the organisation on whose behalf you are making the application or within which you work in your professional capacity as an applicant. This should include a parent organisation, and sub-division or department if appropriate (for example University of Edinburgh, Department of Informatics).", + "question": "Your organisation name", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "input": { + "type": "radioOptionsInput", + "required": true, + "label": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "question": "If yes, please provide the current status", + "input": { "type": "textInput" }, + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitstatus" + }, + { + "question": "If yes, please provide the date published", + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitdatepublished", + "input": { "type": "datePickerCustom" }, + "validations": [{ "type": "isCustomDate" }] + } + ] + }, + { "value": "No", "text": "No" } + ] + }, + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkit", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Will your organisation act as data controller?", + "input": { + "type": "radioOptionsInput", + "label": "Will your organisation act as data controller?", + "options": [ + { + "conditionalQuestions": [ + { + "question": "ICO registered number", + "questionId": "safepeopleprimaryapplicantorganisationicoregisterednumber", + "input": { "type": "textInput" } + }, + { + "question": "Registered address (line 1)", + "guidance": "Please include the organisation's business address", + "questionId": "safepeopleprimaryapplicantorganisationaddressline1", + "input": { "type": "textInput" } + }, + { + "question": "Registered address (line 2)", + "input": { "type": "textInput" }, + "questionId": "safepeopleprimaryapplicantorganisationaddressline2", + "guidance": "Please include the organisation's business address" + }, + { + "question": "City", + "guidance": "Please specify the city where the organisation is located", + "questionId": "safepeopleprimaryapplicantorganisationcity", + "input": { "type": "textInput" } + }, + { + "question": "Postcode", + "guidance": "Please include the organisation's business address postcode.", + "input": { "type": "textInput" }, + "questionId": "safepeopleprimaryapplicantorganisationpostcode" + }, + { + "question": "Country", + "questionId": "safepeopleprimaryapplicantorganisationcountry", + "input": { "type": "textInput" }, + "guidance": "Please specify the country where the organisation is located" + }, + { + "question": "Organisation type", + "label": "Organisation type", + "guidance": "Please select type of organisation, unique purpose or role of the organisation", + "questionId": "safepeopleprimaryapplicantorganisationtype", + "input": { + "options": [ + { "text": "Academic institution", "value": "Academic institution" }, + { "value": "National body", "text": "National body" }, + { "value": "Healthcare provider", "text": "Healthcare provider" }, + { "text": "Healthcare comissioner", "value": "Healthcare comissioner" }, + { "text": "Commercial body", "value": "Commercial body" }, + { "text": "Local Authority", "value": "Local Authority" }, + { "value": "Other", "text": "Other" } + ], + "type": "checkboxOptionsInput" + } + }, + { + "question": "Please provide details", + "input": { "type": "textareaInput" }, + "questionId": "safepeopleprimaryapplicantorganisationtypeotherdetails" + } + ], + "text": "Yes", + "value": "Yes" + }, + { "value": "No", "text": "No" }, + { "value": "Unsure", "text": "Unsure" } + ] + }, + "questionId": "safepeopleprimaryapplicantorganisationdatacontroller", + "guidance": "Please specify if your organisation will act as a data controller. If your organisation is not the sole data controller, please provide details of other data controllers. ", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + }, + { + "questionSetHeader": "Other individuals", + "questionSetId": "safepeople-otherindividuals", + "questions": [ + { + "question": "Full name", + "input": { "type": "textInput" }, + "questionId": "safepeopleotherindividualsfullname", + "guidance": "Full name is the individual's first and last name", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Job title", + "input": { "type": "textInput" }, + "questionId": "safepeopleotherindividualsjobtitle", + "guidance": "Job Title is the name of the position the individual holds within their organisation.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Organisation", + "questionId": "safepeopleotherindividualsorganisation", + "input": { "type": "textInput" }, + "guidance": "Please include the individual's organisation.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { + "type": "checkboxOptionsInput", + "label": "Role", + "options": [ + { "value": "Principal investigator", "text": "Principal investigator" }, + { "text": "Collaborator", "value": "Collaborator" }, + { "value": "Team member", "text": "Team member" }, + { + "value": "Other", + "text": "Other", + "conditionalQuestions": [ + { + "questionId": "safepeopleotherindividualsroleotherdetails", + "input": { "type": "textareaInput" }, + "question": "If other, please specify" + } + ] + } + ] + }, + "questionId": "safepeopleotherindividualsrole", + "guidance": "A role is a function that the applicant plays. It might include role types and accreditation for those that are accessing the secure data and those that are not but would see cleared outputs from the project. \r\n (i.e. project lead, deputy lead, accrediter, researcher, peer reviewer)", + "question": "Role", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Will this person access the data requested?", + "questionId": "safepeopleotherindividualsaccessdata", + "input": { + "options": [ + { "value": "Yes", "text": "Yes" }, + { "text": "No", "value": "No" } + ], + "label": "Will this person access the data requested?", + "type": "radioOptionsInput" + }, + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safepeopleotherindividualsaccreditedresearcher", + "input": { + "options": [ + { + "conditionalQuestions": [ + { + "question": "If yes, please provide details", + "questionId": "safepeopleotherindividualsaccreditedresearcherdetails", + "input": { "type": "textareaInput" } + } + ], + "value": "Yes", + "text": "Yes" + }, + { "value": "No", "text": "No" } + ], + "type": "radioOptionsInput", + "label": "Is this person an accredited researcher under the Digital Economy Act 2017?" + }, + "question": "Is this person an accredited researcher under the Digital Economy Act 2017?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { + "label": "Has this person undertaken professional training or education on the topic of Information Governance?", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safepeopleotherindividualstraininginformationgovernancerecent", + "question": "Please provide full details regarding the most recent training" + } + ] + }, + { + "conditionalQuestions": [ + { + "questionId": "safepeopleotherindividualstraininginformationgovernanceintention", + "input": { "type": "textareaInput" }, + "question": "Please provide any details of plans to attend training, if applicable" + } + ], + "value": "No", + "text": "No" + } + ] + }, + "questionId": "safepeopleotherindividualstraininginformationgovernance", + "question": "Has this person undertaken professional training or education on the topic of Information Governance?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Please provide evidence of this person's expertise and experience relevant to delivering the project", + "input": { "type": "textareaInput" }, + "questionId": "safepeopleotherindividualsexperience", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + }, + { + "questionSetId": "add-safepeople-otherindividuals", + "questions": [ + { + "input": { + "class": "btn btn-primary addButton", + "type": "buttonInput", + "action": "addRepeatableSection", + "text": "Add another individual?", + "panelId": "safepeople-otherindividuals" + }, + "questionId": "add-safepeople-otherindividuals", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + }, + { + "questionSetHeader": "About this application", + "questionSetId": "safeproject-aboutthisapplication", + "questions": [ + { + "question": "This application is...", + "guidance": "The application could be a new application, an extension, a renewal or amendment. For extensions or amendments, you must highlight the specific information within this form that has been updated, provide an original application number and approval date, any subsequent amendment approval dates and a summary of changes and rationale for the change to your original application and any updated approvals signatures in order for the request to be processed.", + "questionId": "safeprojectaboutthisapplicationtype", + "lockedQuestion": 1, + "defaultQuestion": 0, + "input": { + "options": [ + { "text": "A new application", "value": "A new application" }, + { + "conditionalQuestions": [ + { + "questionId": "safeprojectaboutthisapplicationtypeamendmentdetails", + "input": { "type": "textareaInput" }, + "question": "Reference or details of previous application" + } + ], + "text": "An amendment to an existing application", + "value": "An amendment to an existing application" + }, + { + "conditionalQuestions": [ + { + "question": "Reference or details of previous application", + "questionId": "safeprojectaboutthisapplicationtypeextensiondetails", + "input": { "type": "textareaInput" } + } + ], + "value": "An extension of an existing approval", + "text": "An extension of an existing approval" + }, + { + "conditionalQuestions": [ + { + "question": "Reference or details of previous application", + "questionId": "safeprojectaboutthisapplicationtyperenewaldetails", + "input": { "type": "textareaInput" } + } + ], + "text": "A renewal of an existing approval", + "value": "A renewal of an existing approval" + }, + { + "text": "Related to a previous application (approved or not)", + "value": "Related to a previous application (approved or not)", + "conditionalQuestions": [ + { + "question": "Reference or details of previous application", + "questionId": "safeprojectaboutthisapplicationtyperelatedpreviousdetails", + "input": { "type": "textareaInput" } + } + ] + } + ], + "label": "This application is...", + "type": "radioOptionsInput" + } + } + ] + }, + { + "questions": [ + { + "guidance": "The title should identify the main area of your research so that another researcher could understand if it might be relevant to their area of study. \n \nThe titles of all Accredited Research projects are published on the UK Statistics Authority website as part of the public record of DEA Accredited Researchers.", + "validations": [{ "type": "isLength", "params": [{ "$numberDouble": "3.0" }, { "$numberDouble": "300.0" }] }], + "input": { "type": "textInput", "required": true }, + "questionId": "safeprojectprojectdetailstitle", + "question": "Title of project", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "What is the type of project?", + "guidance": "A research project is a discrete scientific endeavor to answer a research question or a set of research questions. \n\nA clinic audit project is designed and conducted to produce information to inform delivery of best care. It aims to find out if healthcare is being provided in line with standards to inform care providers and patients about where a service is doing well, and where there could be improvements.\n\nA service evaluation project is designed and conducted solely to define or judge current care. It seeks to assess current service to assess how well a service is achieving its intended aims.", + "questionId": "safeprojectprojectdetailstype", + "lockedQuestion": 1, + "defaultQuestion": 0, + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "input": { + "options": [ + { "value": "Research", "text": "Research" }, + { "value": "Clinic audit", "text": "Clinic audit" }, + { "value": "Service evaluation", "text": "Service evaluation" }, + { + "conditionalQuestions": [{ "question": "If other, please specify", "input": { "type": "textInput" }, "questionId": "" }], + "value": "Other", + "text": "Other" + } + ], + "type": "radioOptionsInput", + "required": true, + "label": "What is the type of project?" + } + }, + { + "questionId": "safeprojectprojectdetailsneworexisting", + "lockedQuestion": 1, + "defaultQuestion": 0, + "input": { + "label": "Is this a new study or supporting an existing study?", + "type": "radioOptionsInput", + "options": [ + { "value": "New study", "text": "New study" }, + { + "text": "Existing study", + "value": "Existing study", + "conditionalQuestions": [ + { + "question": "Evidence of existing outputs", + "label": "Evidence of existing outputs", + "input": { + "options": [ + { + "value": "I have enclosed evidence of existing outputs", + "text": "I have enclosed evidence of existing outputs" + } + ], + "type": "checkboxOptionsInput" + }, + "questionId": "safeprojectprojectdetailsneworexistingexistingoutputevidence" + } + ] + } + ] + }, + "question": "Is this a new study or supporting an existing study?" + }, + { + "guidance": "Please provide a summary of the study in language suitable for non-experts in the field and ensure that all abbreviations in technical terminology are explained.\n \nThe summary must make clear what the specific purpose is, who will be using the data (organisations rather than individual names), what will happen to the data, whether the expected outputs are in record level form, what is known to date about your chosen project including any preliminary/related analysis and background literature reviews. Please include any potential disclosure risks and how these will be addressed.", + "validations": [{ "type": "isLength", "params": [{ "$numberDouble": "10.0" }, { "$numberDouble": "2000.0" }] }], + "input": { "required": true, "type": "textareaInput" }, + "questionId": "safeprojectprojectdetailslaysummary", + "question": "Please provide a lay summary of the project (300 words)", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "validations": [{ "type": "isCustomDate" }], + "input": { "type": "datePickerCustom" }, + "questionId": "safeproject-projectdetails-startdate", + "question": "What is the anticipated start date of the project?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Please provide anticipated end date of the project?", + "validations": [{ "type": "isCustomDate" }], + "input": { "type": "datePickerCustom" }, + "questionId": "safeproject-projectdetails-enddate", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "What are the project aims, objectives and rationale?", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], + "input": { "type": "textareaInput", "required": true }, + "questionId": "safeprojectprojectdetailsaimsobjectivesrationale", + "guidance": "Please include the background to the project by describing why you are conducting the study, the specific aims and the hypotheses that you hope to test. Summarise how the data requested are required to help address these aims. Please include whether the project has used peer review and if applicable the nature of that review. ", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { "type": "textareaInput", "required": true }, + "validations": [{ "type": "isLength", "message": "Please enter a value", "params": [{ "$numberDouble": "1.0" }] }], + "questionId": "safeprojectprojectdetailsdatausageforobjectives", + "guidance": "Provide full details of your research methodology. This must include justification of sample size, analyses proposed, statistical methods, additional data sources such as linked data and any plans for collaborative work. \n\nThis information will be key to assessing whether your proposal will be feasible, deliver clear public good and be an appropriate use of data. \n\nEnsure you: \n\nSpecify the method(s) of analysis you plan to use (such as regression);\n\nAs far as possible, try to articulate the outcome or dependent variable(s). \n\nIndicate the starting point for the modelling process - acknowledging that the model may evolve.\n\nExplain (where relevant) how any potential selection/causal bias will be addressed (e.g. by including a control group with information on how this control group will be created); \n\nProvide methodology references, if a non-standard methodology is proposed;\n\nInclude information about any contribution to the field of research methodology that you believe may result from your research;\n\nInclude an explanation of how your methodological approach will answer the research question(s) set out in the project when employing methods not covered by any of the above (e.g. correlation or basic descriptive analysis will only be used, noting that such analysis might be more applicable for exploratory research).", + "question": "How will the data requested be used to achieve the project objectives?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "How will your project benefit the public and what is the anticipated impact?", + "guidance": "Use this section to give the background and justification of your proposal, to demonstrate how your project will benefit the public, as well as show your understanding of the Information Governance issues specific and inherent to your project. Please make it clear how the data requested will contribute. \n\nPlease also show that you have considered how to balance the privacy risks and public benefits when designing the study. The requirement for the datasets requested should be fully justified in the light of the aims and objectives of the proposal.", + "input": { "required": true, "type": "textareaInput" }, + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], + "questionId": "safeprojectprojectdetailspublicbenefitimpact", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safeprojectprojectdetailspublicpatientinvolvementengagement", + "input": { "type": "textareaInput" }, + "guidance": "Provide full details of proposed public engagement plans for patient and/or user group involvement. If you have no plans, please elaborate why there will not be public engagement.", + "question": "Can you provide an outline of the public and patient involvement and engagement (PPIE*) strategies of the study or a brief explanation of why they are not planned?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Do you anticipate any risk to individual privacy, and if so what steps have you made in your proposal to mitigate these?", + "input": { "type": "textareaInput" }, + "questionId": "safeprojectprojectdetailsindividualprivacyriskmitigation", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { "type": "textareaInput" }, + "questionId": "safeprojectprojectdetailsgroupindividualdisavantageavoidance", + "question": "In considering your proposal, do you believe it could disadvantage any group or individual? Explain what steps you have taken to avoid this.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Please provide up to 6 keywords which best summarise your proposed research project.", + "input": { "type": "textareaInput" }, + "questionId": "safeprojectprojectdetailsresearchprojectsummarykeywords", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Will the research project enhance future benefit from our research database?", + "input": { "type": "textareaInput" }, + "questionId": "safeprojectprojectdetailsresearchprojectfuturedatabasebenefit", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetId": "safeproject-projectdetails", + "questionSetHeader": "Project details" + }, + { + "questions": [ + { + "question": "Does your project have a funder?", + "questionId": "safeprojectfunderinformationprojecthasfunder", + "lockedQuestion": 1, + "defaultQuestion": 0, + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please select an option", "type": "isLength" }], + "input": { + "label": "Does your project have a funder?", + "required": true, + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "question": "Please provide the organisation name", + "input": { "type": "textInput" }, + "questionId": "safeprojectfunderinformationprojecthasfundername" + }, + { + "questionId": "add-safeproject-funderinformation", + "input": { + "action": "addRepeatableQuestions", + "questionIds": ["safeprojectfunderinformationprojecthasfundername"], + "text": "Add another organisation?", + "separatorText": "Organisation Details", + "class": "btn btn-primary addButton", + "type": "buttonInput" + } + } + ] + }, + { + "text": "No", + "value": "No", + "conditionalQuestions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safeprojectfunderinformationprojecthasnofunderfundingsource", + "question": "If no, please provide details of how you intend to fund the study" + }, + { + "label": "Please provide evidence of independent peer review", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I confirm I have provided evidence of independent peer review.", + "text": "I confirm I have provided evidence of independent peer review." + } + ] + }, + "questionId": "safeprojectfunderinformationprojecthasnofunderindependentpeerreviewevidence", + "question": "Please provide evidence of independent peer review" + } + ] + } + ] + } + } + ], + "questionSetHeader": "Funder information", + "questionSetId": "safeproject-funderinformation" + }, + { + "questions": [ + { + "question": "Does your project have a sponsor?", + "questionId": "safeprojectsponsorinformationprojecthassponsor", + "lockedQuestion": 1, + "defaultQuestion": 0, + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "input": { + "type": "radioOptionsInput", + "required": true, + "label": "Does your project have a sponsor?", + "options": [ + { + "conditionalQuestions": [ + { + "questionId": "safeprojectsponsorinformationprojecthassponsorapplicant", + "input": { "type": "textInput" }, + "question": "Associated applicant" + }, + { + "question": "Organisation name", + "input": { "type": "textInput" }, + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationname" + }, + { + "question": "Registered address (line 1)", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", + "input": { "type": "textInput" } + }, + { + "question": "Registered address (line 2)", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", + "input": { "type": "textInput" } + }, + { + "input": { "type": "textInput" }, + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcity", + "question": "City" + }, + { + "question": "Postcode", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationpostcode", + "input": { "type": "textInput" } + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcountry", + "input": { "type": "textInput" }, + "question": "Country" + }, + { + "question": "Sector", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsector", + "input": { "type": "textInput" }, + "guidance": "Please provide details of the sponsor's sector e.g. NHS, Academia, Charity, Industry." + }, + { + "question": "Size", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsize", + "input": { "type": "textInput" } + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails", + "input": { "type": "textInput" }, + "question": "Additional details" + }, + { + "question": "Contact full name", + "questionId": "safeprojectsponsorinformationprojecthassponsorcontactfullname", + "input": { "type": "textInput" } + }, + { + "question": "Contact email address", + "input": { "type": "textInput" }, + "questionId": "safeprojectsponsorinformationprojecthassponsorcontactemail" + }, + { + "question": "Contact telephone", + "input": { "type": "textInput" }, + "questionId": "safeprojectsponsorinformationprojecthassponsorcontacttelephone" + }, + { + "questionId": "add-safeproject-sponsorinformation", + "input": { + "action": "addRepeatableQuestions", + "text": "Add another organisation?", + "questionIds": [ + "safeprojectsponsorinformationprojecthassponsorapplicant", + "safeprojectsponsorinformationprojecthassponsororganisationname", + "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", + "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", + "safeprojectsponsorinformationprojecthassponsororganisationcity", + "safeprojectsponsorinformationprojecthassponsororganisationpostcode", + "safeprojectsponsorinformationprojecthassponsororganisationcountry", + "safeprojectsponsorinformationprojecthassponsororganisationsector", + "safeprojectsponsorinformationprojecthassponsororganisationsize", + "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails", + "safeprojectsponsorinformationprojecthassponsorcontactfullname", + "safeprojectsponsorinformationprojecthassponsorcontactemail", + "safeprojectsponsorinformationprojecthassponsorcontacttelephone" + ], + "separatorText": "Organisation Details", + "class": "btn btn-primary addButton", + "type": "buttonInput" + } + } + ], + "text": "Yes", + "value": "Yes" + }, + { "value": "No", "text": "No" } + ] + } + } + ], + "questionSetHeader": "Sponsor information", + "questionSetId": "safeproject-sponsorinformation" + }, + { + "questions": [ + { + "question": "Is there a commercial interest in this project?", + "questionId": "safeprojectdeclarationofinteresthascommercialinterest", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "lockedQuestion": 1, + "defaultQuestion": 0, + "input": { + "label": "Is there a commercial interest in this project?", + "required": true, + "type": "radioOptionsInput", + "options": [ + { + "value": "Yes", + "text": "Yes", + "conditionalQuestions": [ + { + "question": "Organisation name", + "input": { "type": "textInput" }, + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationname" + }, + { + "question": "Registered address (line 1)", + "input": { "type": "textInput" }, + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1" + }, + { + "input": { "type": "textInput" }, + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", + "question": "Registered address (line 2)" + }, + { + "question": "Postcode", + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", + "input": { "type": "textInput" } + }, + { + "question": "City", + "input": { "type": "textInput" }, + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcity" + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry", + "input": { "type": "textInput" }, + "question": "Country" + }, + { + "question": "Describe the nature of interest", + "questionId": "safeprojectdeclarationofinteresthascommercialinterestinterestnature", + "input": { "type": "textareaInput" } + }, + { + "label": "Public interest", + "questionId": "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest", + "input": { + "options": [ + { + "value": "I confirm that any commercial interest is public interest related.", + "text": "I confirm that any commercial interest is public interest related." + } + ], + "type": "checkboxOptionsInput" + }, + "question": "Public interest" + }, + { + "input": { + "questionIds": [ + "safeprojectdeclarationofinteresthascommercialinterestorganisationname", + "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1", + "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", + "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", + "safeprojectdeclarationofinteresthascommercialinterestorganisationcity", + "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry", + "safeprojectdeclarationofinteresthascommercialinterestinterestnature", + "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest" + ], + "text": "Add another organisation?", + "action": "addRepeatableQuestions", + "type": "buttonInput", + "class": "btn btn-primary addButton", + "separatorText": "Organisation Details" + }, + "questionId": "add-safeproject-declarationofinterest" + } + ] + }, + { "value": "No", "text": "No" } + ] + } + } + ], + "questionSetHeader": "Declaration of interest", + "questionSetId": "safeproject-declarationofinterest" + }, + { + "questionSetHeader": "Intellectual property", + "questionSetId": "safeproject-intellectualproperty", + "questions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safeprojectintellectualpropertydevelopmentnewproductprocess", + "question": "Please indicate if the research could lead to the development of a new product/process or the generation of intellectual property." + } + ] + }, + { + "questionSetId": "safedata-datafields", + "questionSetHeader": "Data fields", + "questions": [ + { + "questionId": "safedatadatafieldsdatarequiredjustification", + "input": { "type": "textareaInput" }, + "question": "Please indicate the data necessary to conduct the study, the data fields required and the justifications for each field.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safedatadatafieldsfileuploaded", + "input": { + "options": [ + { + "value": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field.", + "text": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field." + } + ], + "label": "Data fields indicated via file upload", + "type": "checkboxOptionsInput" + }, + "question": "Data fields indicated via file upload", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "guidance": "A description of precisely the criteria which define the patients to be included and to be excluded from the data extract you are requesting should be provided. \n\nThis should include precise date parameters for the start and end of the range requested (dd/mm/yy) and explain which dated project field will be used to define the requested cohort (e.g. date of admission or date of operation).", + "questionId": "safedatadatafieldsinclusionexclusioncriteria", + "validations": [{ "type": "isLength", "message": "Please enter a value", "params": [{ "$numberDouble": "1.0" }] }], + "input": { "type": "textareaInput", "required": true }, + "question": "Inclusion and exclusion criteria (including date parameters)", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Will you require periodic refreshes of the data?", + "questionId": "safedatadatafieldsdatarefreshrequired", + "validations": [{ "message": "Please select an option", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], + "input": { + "label": "Will you require periodic refreshes of the data?", + "type": "radioOptionsInput", + "required": true, + "options": [ + { + "conditionalQuestions": [ + { + "guidance": "Please indicate how often data refreshes will be needed.", + "questionId": "safedatadatafieldsdatarefreshrequiredperiodicity", + "input": { + "label": "How often will the data refreshes be needed?", + "type": "radioOptionsInput", + "options": [ + { "text": "Every month", "value": "Every month" }, + { "text": "Every 3 months", "value": "Every 3 months" }, + { "text": "Every 6 months", "value": "Every 6 months" }, + { "text": "Every 12 months", "value": "Every 12 months" }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ + { + "questionId": "safedatadatafieldsdatarefreshrequiredperiodicityotherdetails", + "input": { "type": "textInput" }, + "question": "If other, please specify" + } + ] + } + ] + }, + "question": "How often will the data refreshes be needed?" + } + ], + "value": "Yes", + "text": "Yes" + }, + { "value": "No", "text": "No" } + ] + }, + "guidance": "Please indicate if data refreshes will be required.", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + }, + { + "questions": [ + { + "input": { + "label": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", + "type": "radioOptionsInput", + "required": true, + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "input": { "type": "textareaInput" }, + "questionId": "safedataotherdatasetslinkadditionaldatasetslinkagedetails", + "question": "Specify all datasets, organisations which will perform the linkage and how the linkage will take place." + } + ] + }, + { "value": "No", "text": "No" } + ] + }, + "validations": [{ "type": "isLength", "message": "Please select an option", "params": [{ "$numberDouble": "1.0" }] }], + "questionId": "safedataotherdatasetslinkadditionaldatasets", + "question": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safedataotherdatasetsrisksmitigations", + "input": { "type": "textareaInput" }, + "question": "Please summarise the risks/mitigations considered.", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetId": "safedata-otherdatasetsintentiontolinkdata", + "questionSetHeader": "Other datasets - Intention to link data" + }, + { + "questions": [ + { + "guidance": "The lawful basis for processing are set out in Article 6 of the GDPR. At least one legal basis must apply whenever you process personal data. Please select appropriate Article 6 lawful basis. Processing shall be lawful only if and to the extent that at least one of the following applies.", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "input": { + "required": true, + "type": "radioOptionsInput", + "label": "Article 6 lawful basis", + "options": [ + { "text": "Not applicable", "value": "Not applicable" }, + { + "value": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;", + "text": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;" + }, + { + "value": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;", + "text": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;" + }, + { + "value": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;", + "text": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;" + }, + { + "value": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;", + "text": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;" + }, + { + "value": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;", + "text": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;" + }, + { + "value": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child.", + "text": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child." + } + ] + }, + "questionId": "safedatalawfulbasisgdprarticle6basis", + "question": "Article 6 lawful basis", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "guidance": "Please provide justification for selected Article 6 lawful basis.", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], + "input": { "type": "textareaInput", "required": true }, + "questionId": "safedatalawfulbasisgdprarticle6basisjustification", + "question": "Article 6 legal basis justification", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "guidance": "Please select appropriate Article 9 conditions. \n \nProcessing of personal data revealing racial or ethnic origin, political opinions, religious or philosophical beliefs, or trade union membership, and the processing of genetic data, biometric data for the purpose of uniquely identifying a natural person, data concerning health or data concerning a natural person's sex life or sexual orientation shall be prohibited. This does not apply if one of the following applies.", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "questionId": "safedatalawfulbasisgdprarticle9conditions", + "input": { + "label": "Article 9 conditions", + "type": "radioOptionsInput", + "required": true, + "options": [ + { "text": "Not applicable", "value": "Not applicable" }, + { + "value": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;", + "text": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;" + }, + { + "value": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;", + "text": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;" + }, + { + "text": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;", + "value": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;" + }, + { + "text": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;", + "value": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;" + }, + { + "text": "(e) processing relates to personal data which are manifestly made public by the data subject;", + "value": "(e) processing relates to personal data which are manifestly made public by the data subject;" + }, + { + "text": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;", + "value": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;" + }, + { + "text": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;", + "value": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;" + }, + { + "text": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;", + "value": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;" + }, + { + "value": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;", + "text": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;" + }, + { + "value": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject.", + "text": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject." + } + ] + }, + "question": "Article 9 conditions", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "question": "Article 9 legal basis justification", + "guidance": "Please provide justification for selected Article 9 conditions.", + "questionId": "safedatalawfulbasisgdprarticle9basisjustification", + "validations": [{ "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], + "input": { "type": "textareaInput", "required": true }, + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetHeader": "Lawful basis", + "questionSetId": "safedata-lawfulbasis" + }, + { + "questionSetHeader": "Confidentiality avenue", + "questionSetId": "safedata-confidentialityavenue", + "questions": [ + { + "validations": [{ "message": "Please select an option", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], + "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformation", + "input": { + "type": "radioOptionsInput", + "required": true, + "label": "Please provide the legal basis to process confidential information", + "options": [ + { "text": "Not applicable", "value": "Not applicable" }, + { + "conditionalQuestions": [ + { + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested", + "text": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested" + } + ] + }, + "questionId": "safedataconfidentialityavenueinformedconsentevidence", + "label": "Informed consent evidence", + "question": "Informed consent evidence" + } + ], + "text": "Informed consent", + "value": "Informed consent" + }, + { + "value": "Section 251 support", + "text": "Section 251 support", + "conditionalQuestions": [ + { + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "text": "I have enclosed a copy of the S251 approved amendments and any renewal letters", + "value": "I have enclosed a copy of the S251 approved amendments and any renewal letters" + } + ] + }, + "questionId": "safedataconfidentialityavenuesection251exemptionevidence", + "label": "Section 251 exemption evidence", + "question": "Section 251 exemption evidence" + }, + { + "question": "CAG reference", + "questionId": "safedataconfidentialityavenuesection251cagreference", + "input": { "type": "textInput" } + }, + { + "question": "The section 251 approval enables the applicant to", + "questionId": "safedataconfidentialityavenuesection251approvaldetails", + "input": { + "options": [ + { "text": "Hold/receive personal data", "value": "Hold/receive personal data" }, + { "text": "Transfer/access personal data", "value": "Transfer/access personal data" }, + { "value": "Operate on and link personal data", "text": "Operate on and link personal data" }, + { + "conditionalQuestions": [ + { + "question": "If other, please specify", + "input": { "type": "textInput" }, + "questionId": "safedataconfidentialityavenuesection251approvaldetailsotherdetails" + } + ], + "value": "Other", + "text": "Other" + } + ], + "type": "checkboxOptionsInput" + }, + "label": "The section 251 approval enables the applicant to" + } + ] + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ + { + "question": "If other, please specify", + "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformationotherdetails", + "input": { "type": "textInput" } + } + ] + } + ] + }, + "question": "Please provide the legal basis to process confidential information", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + }, + { + "questions": [ + { + "question": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?", + "input": { + "options": [ + { "value": "Yes", "text": "Yes" }, + { "value": "No", "text": "No" } + ], + "type": "radioOptionsInput", + "label": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?" + }, + "questionId": "safedataethicsapprovalinsightresearch", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "validations": [{ "type": "isLength", "message": "Please select an option", "params": [{ "$numberDouble": "1.0" }] }], + "questionId": "safedataethicsapprovalapprovalobtained", + "input": { + "options": [ + { + "conditionalQuestions": [ + { + "guidance": "Please provide REC or other committee details.", + "input": { "type": "textInput" }, + "questionId": "safedataethicsapprovalapprovalobtainedreccommitteename", + "question": "Approval - REC committee name" + }, + { + "questionId": "safedataethicsapprovalapprovalobtainedrecreferencenumber", + "input": { "type": "textInput" }, + "question": "Approval - REC reference number" + }, + { + "question": "Approval - Other committee", + "questionId": "safedataethicsapprovalapprovalobtainedothercommittee", + "input": { "type": "textInput" } + }, + { + "label": "Evidence of REC approval", + "guidance": "Please confirm a copy of the REC referenced above has been enclosed.", + "input": { + "options": [ + { + "text": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments", + "value": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments" + } + ], + "type": "checkboxOptionsInput" + }, + "questionId": "safedataethicsapprovalapprovalobtainedevidenceenclosed", + "question": "Evidence of REC approval" + } + ], + "value": "Yes", + "text": "Yes" + }, + { + "text": "No", + "value": "No", + "conditionalQuestions": [ + { + "questionId": "safedataethicsapprovalapprovalobtainednotobtaineddetails", + "input": { "type": "textInput" }, + "question": "If not, please provide more details" + } + ] + }, + { + "text": "Approval pending", + "value": "Approval pending", + "conditionalQuestions": [ + { + "question": "If approval is pending, please provide more details", + "input": { "type": "textInput" }, + "questionId": "safedataethicsapprovalapprovalobtainedpendingdetails", + "guidance": "If approval is pending, please provide details." + } + ] + }, + { + "value": "Not required", + "text": "Not required", + "conditionalQuestions": [ + { + "question": "If not required, please provide details", + "guidance": "If ethics approval is not required, please explain why this is the case.", + "questionId": "safedataethicsapprovalapprovalobtainednotrequireddetails", + "input": { "type": "textInput" } + } + ] + } + ], + "label": "Has ethical approval been obtained?", + "type": "radioOptionsInput", + "required": true + }, + "guidance": "Please confirm if ethics approval has been obtained. Request for research purposes must include evidence of ethics approval or evidence that this is not required.", + "question": "Has ethical approval been obtained?", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetHeader": "Ethical approval", + "questionSetId": "safedata-ethicalapproval" + }, + { + "questions": [ + { + "guidance": "Please specify if the data will be accessed within a Trusted Research Environment. ", + "input": { + "options": [ + { + "conditionalQuestions": [ + { + "guidance": "If you have selected 'Other', please specify the Trusted Research Environment.", + "input": { + "type": "radioOptionsInput", + "label": "In which Trusted Research Environment will the data be accessed?", + "options": [ + { "value": "Secure e-Research Platform (SeRP)", "text": "Secure e-Research Platform (SeRP)" }, + { "value": "NI Honest Broker Service (NI HBS)", "text": "NI Honest Broker Service (NI HBS)" }, + { "text": "Scottish National Safe Haven (SNSH)", "value": "Scottish National Safe Haven (SNSH)" }, + { + "text": "NHS Digital", + "value": "NHS Digital", + "conditionalQuestions": [ + { + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentdspdetails", + "input": { "type": "textInput" }, + "question": "Does the applicant organisation have a DSP Toolkit? If so, please provide details including code, score and version completed." + } + ] + }, + { "text": "SAIL Databank", "value": "SAIL Databank" }, + { "value": "ONS Secure Research Service (SRS)", "text": "ONS Secure Research Service (SRS)" }, + { + "value": "Other", + "text": "Other", + "conditionalQuestions": [ + { + "question": "If other, please specify", + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentotherdetails", + "input": { "type": "textInput" } + } + ] + } + ] + }, + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironment", + "question": "In which Trusted Research Environment will the data be accessed?" + } + ], + "text": "Via a Trusted Research Environment", + "value": "Via a Trusted Research Environment" + }, + { + "conditionalQuestions": [ + { + "question": "Registered name of organisation", + "input": { "type": "textInput" }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationname", + "guidance": "Please provide ICO registration details." + }, + { + "question": "Registered number", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber", + "input": { "type": "textInput" } + }, + { + "question": "Will this organisation be storing or processing the data?", + "label": "Will this organisation be storing or processing the data?", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", + "input": { + "options": [ + { "value": "Storage", "text": "Storage" }, + { "text": "Processing", "value": "Processing" } + ], + "type": "checkboxOptionsInput" + } + }, + { + "label": "What type of security assurance does this organisation have in place?", + "guidance": "Adequate security assurance must be provided for all processing locations. Each organisation processing data that is not fully anonymous as part of this project must demonstrate that they have appropriate security arrangements are in place. Please confirm whether the applicant organisation has a compliant Data Security and Protection Toolkit.", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "text": "Data security and Protection Toolkit (DSP Toolkit)", + "value": "Data security and Protection Toolkit (DSP Toolkit)", + "conditionalQuestions": [ + { + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsporganisationcode", + "input": { "type": "textInput" }, + "guidance": "As a data controller, the applicant's organisation should be registered with the Information Commissioner's Office (ICO). Please provide Security and Protection Toolkit (DSP Toolkit) details.", + "question": "DSP Toolkit organisation code" + }, + { + "question": "DSP Toolkit score", + "input": { "type": "textInput" }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsptoolkitscore" + }, + { + "question": "DSP Toolkit version completed", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedspversioncompleted", + "input": { "type": "textInput" } + } + ] + }, + { + "text": "ISO 27001", + "value": "ISO 27001", + "conditionalQuestions": [ + { + "label": "Evidence of ISO 27001", + "input": { + "options": [ + { + "text": "I have enclosed a copy of my certificate", + "value": "I have enclosed a copy of my certificate" + } + ], + "type": "checkboxOptionsInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceiso27001evidence", + "question": "Evidence of ISO 27001" + } + ] + }, + { + "conditionalQuestions": [ + { + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceslspevidence", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I have enclosed a completed system level security policy for ODR review", + "text": "I have enclosed a completed system level security policy for ODR review" + } + ] + }, + "label": "Evidence of SLSP", + "question": "Evidence of SLSP" + } + ], + "text": "SLSP", + "value": "SLSP" + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ + { + "question": "If other, please specify", + "guidance": "If you have selected 'Other', please specify the type of security assurance the organisation has put in place.", + "input": { "type": "textInput" }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceotherdetails" + } + ] + } + ] + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance", + "question": "What type of security assurance does this organisation have in place?" + }, + { + "questionId": "add-safesettings-storageandprocessing", + "input": { + "separatorText": "Organisation Details", + "type": "buttonInput", + "class": "btn btn-primary addButton", + "action": "addRepeatableQuestions", + "questionIds": [ + "safedatastorageandprocessingaccessmethodphysicallocationorganisationname", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance" + ], + "text": "Add another organisation?" + } + } + ], + "value": "Via transfer to a physical location (Provide details of the processing/storage organisations below)", + "text": "Via transfer to a physical location (Provide details of the processing/storage organisations below)" + } + ], + "label": "How will the data be accessed?", + "type": "checkboxOptionsInput" + }, + "questionId": "safedatastorageandprocessingaccessmethod", + "question": "How will the data be accessed?", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetId": "safesettings-storageandprocessing", + "questionSetHeader": "Storage and processing" + }, + { + "questions": [ + { + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please select an option", "type": "isLength" }], + "input": { + "options": [ + { + "value": "Yes", + "text": "Yes", + "conditionalQuestions": [ + { + "question": "If yes, please provide more details", + "input": { "type": "textareaInput" }, + "questionId": "safedatadataflowdatatransferedoutsideukdetails" + } + ] + }, + { "value": "No", "text": "No" } + ], + "label": "Will the data be transferred outside of the United Kingdom?", + "type": "radioOptionsInput", + "required": true + }, + "questionId": "safedatadataflowdatatransferedoutsideuk", + "question": "Will the data be transferred outside of the United Kingdom?", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safedatadataflowregionsdataprocessed", + "input": { + "type": "checkboxOptionsInput", + "label": "Please specify the regions where data will be processed.", + "options": [ + { "text": "England/Wales", "value": "England/Wales" }, + { "value": "United Kingdom", "text": "United Kingdom" }, + { "value": "European Economic Area", "text": "European Economic Area" }, + { "value": "Other", "text": "Other" } + ] + }, + "question": "Please specify the regions where data will be processed.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { "type": "textareaInput" }, + "questionId": "safedatadataflowdetailedinformation", + "question": "Please provide detailed information on data flows", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "questionId": "safedatadataflowdiagramenclosed", + "input": { + "options": [{ "value": "I have enclosed a copy of the dataflow", "text": "I have enclosed a copy of the dataflow" }], + "label": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", + "type": "checkboxOptionsInput" + }, + "question": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetHeader": "Dataflow", + "questionSetId": "safesettings-dataflow" + }, + { + "questions": [ + { + "question": "How will proposal findings be disseminated, to what audience and in what format?", + "questionId": "safeoutputsoutputsdisseminationplansproposalfindings", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], + "input": { "required": true, "type": "textareaInput" }, + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], + "input": { "type": "textareaInput", "required": true }, + "questionId": "safeoutputsoutputsdisseminationplansmilestones", + "question": "Please include any milestones for outputs dissemination.", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], + "input": { "type": "textareaInput", "required": true }, + "questionId": "safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy", + "question": "What steps will be taken to ensure that individuals cannot be identified? Please describe what disclosure control policy will be applied.", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ], + "questionSetId": "safeoutputs-outputsdisseminationplans", + "questionSetHeader": "Outputs dissemination plans" + }, + { + "questionSetHeader": "Retention", + "questionSetId": "safeoutputs-retention", + "questions": [ + { + "question": "Please state the date until which you will retain the data", + "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], + "questionId": "safeoutputs-dataretention-retaindatadate", + "input": { "required": true, "type": "datePickerCustom" }, + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { "type": "textareaInput" }, + "questionId": "safeoutputsdataretentionretaindatadatereason", + "question": "Please indicate the reason for this date", + "lockedQuestion": 1, + "defaultQuestion": 0 + }, + { + "input": { "type": "textareaInput" }, + "questionId": "safeoutputsdataretentionretaindataextensionpermissions", + "question": "Please provide details of any permissions that will need to apply for an extension during this period in order to retain a legal basis to hold the data (e.g. section 251)", + "lockedQuestion": 1, + "defaultQuestion": 0 + } + ] + } + ] + } +} diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index 23f7a8ed..44b4f152 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -11,39 +11,23 @@ export default class QuestionbankController extends Controller { async getQuestionbank(req, res) { try { - // Extract id parameter from query string - const { id } = req.params; - // If no id provided, it is a bad request - if (!id) { - return res.status(400).json({ - success: false, - message: 'You must provide a questionbank identifier', - }); - } - // Find the questionbank - const options = { lean: true }; - const questionbank = await this.questionbankService.getQuestionbank(id, req.query, options); - // Return if no questionbank found - if (!questionbank) { - return res.status(404).json({ - success: false, - message: 'A questionbank could not be found with the provided id', - }); - } - // Return the questionbank + const { publisherId } = req.params; + + const result = await this.questionbankService.getQuestionBankInfo(publisherId); + return res.status(200).json({ success: true, - ...questionbank + result, }); } catch (err) { - // Return error response if something goes wrong - console.error(err.message); - return res.status(500).json({ + // 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', }); - } - } + } + } async getQuestionbanks(req, res) { try { diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index c9508d7d..55632d64 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -1,6 +1,6 @@ import express from 'express'; import passport from 'passport'; -import { isUndefined } from 'lodash'; +import { isUndefined, isNull } from 'lodash'; import QuestionbankController from './questionbank.controller'; import { questionbankService } from './dependency'; import { logger } from '../utilities/logger'; @@ -9,20 +9,40 @@ const router = express.Router(); const questionbankController = new QuestionbankController(questionbankService); const logCategory = 'questionbank'; +function isUserMemberOfTeam(user, teamId) { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); +} + const validateViewRequest = (req, res, next) => { - const { teamId } = req.params; - if (isUndefined(teamId)) return res.status(400).json({ success: false, message: 'You must provide a valid teamId' }); + const { publisherId } = req.params; + + if (isUndefined(publisherId)) return res.status(400).json({ success: false, message: 'You must provide a valid publisher Id' }); next(); }; -const authorizeView = (req, res, next) => {}; +const authorizeView = (req, res, next) => { + const requestingUser = req.user; + const { publisherId } = req.params; + + const authorised = isUserMemberOfTeam(requestingUser, publisherId); + + if (!authorised) { + return res.status(401).json({ + success: false, + message: 'You are not authorised to perform this action', + }); + } + + next(); +}; -// @route GET /api/v1/questionbanks/teamId -// @desc Returns a questionbank based on ID of the publisher provided +// @route GET /api/v1/questionbanks/publisherId +// @desc Returns questionbank info belonging to the publisher // @access Public router.get( - '/:teamId', + '/:publisherId', passport.authenticate('jwt'), validateViewRequest, authorizeView, diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index c39a0b0a..7f4a0e02 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,6 +1,11 @@ +import { isNull, isEmpty } from 'lodash'; +import { DataRequestSchemaModel } from '../datarequest/schema/datarequest.schemas.model'; + export default class QuestionbankService { - constructor(questionbankRepository) { + constructor(questionbankRepository, publisherService, globalService) { this.questionbankRepository = questionbankRepository; + this.publisherService = publisherService; + this.globalService = globalService; } getQuestionbank(id, query = {}, options = {}) { @@ -14,4 +19,136 @@ export default class QuestionbankService { getQuestionbanks(query = {}) { return this.questionbankRepository.getQuestionbanks(query); } + + async getQuestionBankInfo(publisherId) { + const publisher = await this.publisherService.getPublisher(publisherId); + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const masterSchema = global.masterSchema; + + let dataRequestSchemas = await DataRequestSchemaModel.find({ publisher: publisher.name }).sort({ version: -1 }); + + if (isEmpty(dataRequestSchemas)) { + let questionStatus = {}; + + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + questionStatus[question.questionId] = question.defaultQuestion; + }); + }); + + const newSchema = new DataRequestSchemaModel({ + publisher: publisher.name, + status: 'draft', + isCloneable: true, + questionStatus, + }); + + await DataRequestSchemaModel.create(newSchema); + + return { + masterSchema, + questionStatus: newSchema.questionStatus, + guidance: newSchema.guidance, + countOfChanges: newSchema.countOfChanges, + }; + } + + const latestSchemaVersion = dataRequestSchemas[0]; + if (latestSchemaVersion.status === 'draft') { + //check if new questions added in the masterSchema + let newQuestionStatus = latestSchemaVersion.questionStatus; + let newQuestionsAdded = false; + + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + if (!Object.keys(latestSchemaVersion.questionStatus).includes(question.questionId)) { + newQuestionStatus[question.questionId] = question.defaultQuestion; + newQuestionsAdded = true; + } + }); + }); + + if (newQuestionsAdded) + await DataRequestSchemaModel.findOneAndUpdate({ _id: latestSchemaVersion._id }, { $set: { questionStatus: newQuestionStatus } }); + + return { + masterSchema, + questionStatus: newQuestionStatus, + guidance: latestSchemaVersion.guidance, + countOfChanges: latestSchemaVersion.countOfChanges, + }; + } + + if (latestSchemaVersion.status === 'active') { + if (!isEmpty(latestSchemaVersion.questionStatus)) { + let newQuestionStatus = latestSchemaVersion.questionStatus; + + //check for new questions in the master schema and add those + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + if (!Object.keys(latestSchemaVersion.questionStatus).includes(question.questionId)) { + newQuestionStatus[question.questionId] = question.defaultQuestion; + } + }); + }); + + const newSchema = new DataRequestSchemaModel({ + publisher: publisher.name, + status: 'draft', + isCloneable: true, + questionStatus: newQuestionStatus, + guidance: latestSchemaVersion.guidance, + version: latestSchemaVersion.version + 1, + }); + + await DataRequestSchemaModel.create(newSchema); + + return { + masterSchema, + questionStatus: newSchema.questionStatus, + guidance: newSchema.guidance, + countOfChanges: newSchema.countOfChanges, + }; + } else { + //need to create the question status from the current jsonSchema + let questionStatus = {}; + const guidance = {}; + const jsonSchema = latestSchemaVersion.jsonSchema; + + let questionIds = []; + jsonSchema.questionSets.forEach(questionSet => { + questionIds = [...questionIds, ...questionSet.questions.map(question => question.questionId)]; + }); + + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + if (questionIds.includes(question.questionId)) { + questionStatus[question.questionId] = 1; + } else { + questionStatus[question.questionId] = question.defaultQuestion; + } + }); + }); + + const newSchema = new DataRequestSchemaModel({ + publisher: publisher.name, + status: 'draft', + isCloneable: true, + questionStatus, + guidance, + version: latestSchemaVersion.version + 1, + }); + + await DataRequestSchemaModel.create(newSchema); + + return { + masterSchema, + questionStatus, + guidance: {}, + countOfChanges: 0, + version: latestSchemaVersion.version + 1, + }; + } + } + } } From fd813204d84e712cd4ee2acc90afe70ce14de7bd Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Wed, 24 Nov 2021 11:07:01 +0000 Subject: [PATCH 003/389] Add tests for question bank service --- .../datarequest/datarequest.repository.js | 15 +- src/resources/publisher/publisher.service.js | 2 +- .../questionbank/__mocks__/questionbank.js | 303 ++++++++++++++++++ .../__tests__/questionbank.service.test.js | 63 ++++ src/resources/questionbank/dependency.js | 3 +- .../questionbank/questionbank.service.js | 36 ++- 6 files changed, 402 insertions(+), 20 deletions(-) create mode 100644 src/resources/questionbank/__mocks__/questionbank.js create mode 100644 src/resources/questionbank/__tests__/questionbank.service.test.js diff --git a/src/resources/datarequest/datarequest.repository.js b/src/resources/datarequest/datarequest.repository.js index 97be596e..9a88fe78 100644 --- a/src/resources/datarequest/datarequest.repository.js +++ b/src/resources/datarequest/datarequest.repository.js @@ -106,7 +106,7 @@ export default class DataRequestRepository extends Repository { ]); } - getApplicationToSubmitById(id) { + getApplicationToSubmitById(id) { return DataRequestModel.findOne({ _id: id }).populate([ { path: 'datasets dataset initialDatasets', @@ -165,6 +165,19 @@ export default class DataRequestRepository extends Repository { }).sort({ createdAt: -1 }); } + getApplicationFormSchemas(publisher) { + return DataRequestSchemaModel.find({ publisher: publisher.name }).sort({ version: -1 }); + } + + createApplicationFormSchema(newSchema) { + const newSchemaModel = new DataRequestSchemaModel(newSchema); + DataRequestSchemaModel.create(newSchemaModel); + } + + updateApplicationFormSchemaById(id, data, options = {}) { + DataRequestSchemaModel.findByIdAndUpdate(id, data, { ...options }); + } + getDatasetsForApplicationByIds(datasetIds) { return ToolModel.find({ datasetid: { $in: datasetIds }, diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index f56f5a5c..c503938b 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -5,7 +5,7 @@ export default class PublisherService { this.publisherRepository = publisherRepository; } - getPublisher(id, options = {}) { + async getPublisher(id, options = {}) { return this.publisherRepository.getPublisher(id, options); } diff --git a/src/resources/questionbank/__mocks__/questionbank.js b/src/resources/questionbank/__mocks__/questionbank.js new file mode 100644 index 00000000..692754a8 --- /dev/null +++ b/src/resources/questionbank/__mocks__/questionbank.js @@ -0,0 +1,303 @@ +export const publisherStub = { + _id: '5f3f98068af2ef61552e1d75', + name: 'ALLIANCE > SAIL', + active: true, + imageURL: '', + allowsMessaging: true, + workflowEnabled: true, + allowAccessRequestManagement: true, + publisherDetails: { name: 'SAIL', memberOf: 'ALLIANCE' }, + uses5Safes: true, + mdcFolderId: 'c4f50de0-2188-426b-a6cd-6b11a8d6c3cb', +}; + +export const globalStub = { + masterSchema: { + questionSets: [ + { + questionSetId: 'primaryapplicant', + questionSetHeader: 'Primary applicant', + questions: [ + { + guidance: 'Please insert your full name.', + validations: [{ message: 'Please enter a value', type: 'isLength', params: [{ $numberDouble: '1.0' }] }], + questionId: 'safepeopleprimaryapplicantfullname', + input: { type: 'textInput', required: true }, + question: 'Full name', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + question: 'Job title', + questionId: 'safepeopleprimaryapplicantjobtitle', + validations: [{ params: [{ $numberDouble: '1.0' }], type: 'isLength', message: 'Please enter a value' }], + input: { required: true, type: 'textInput' }, + lockedQuestion: 1, + defaultQuestion: 1, + }, + { + questionId: 'safepeopleprimaryapplicanttelephone', + input: { type: 'textInput' }, + question: 'Telephone', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + question: 'ORCID', + input: { type: 'textInput' }, + questionId: 'safepeopleprimaryapplicantorcid', + guidance: + 'ORCID provides a persistent digital identifier (an ORCID iD) that you own and control, and that distinguishes you from every other researcher. You can create an ORCID profile at https://orcid.org/. If you have an ORCID iD please include it here. ', + lockedQuestion: 0, + defaultQuestion: 1, + }, + { + question: 'Email', + validations: [{ message: 'Please enter a value', type: 'isLength', params: [{ $numberDouble: '1.0' }] }, { type: 'isEmail' }], + input: { required: true, type: 'textInput' }, + questionId: 'safepeopleprimaryapplicantemail', + lockedQuestion: 1, + defaultQuestion: 0, + }, + ], + }, + { + questionSetHeader: 'Other individuals', + questionSetId: 'safepeople-otherindividuals', + questions: [ + { + question: 'Full name', + input: { type: 'textInput' }, + questionId: 'safepeopleotherindividualsfullname', + guidance: "Full name is the individual's first and last name", + lockedQuestion: 1, + defaultQuestion: 1, + }, + { + question: 'Job title', + input: { type: 'textInput' }, + questionId: 'safepeopleotherindividualsjobtitle', + guidance: 'Job Title is the name of the position the individual holds within their organisation.', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + question: 'Organisation', + questionId: 'safepeopleotherindividualsorganisation', + input: { type: 'textInput' }, + guidance: "Please include the individual's organisation.", + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + input: { + type: 'checkboxOptionsInput', + label: 'Role', + options: [ + { value: 'Principal investigator', text: 'Principal investigator' }, + { text: 'Collaborator', value: 'Collaborator' }, + { value: 'Team member', text: 'Team member' }, + { + value: 'Other', + text: 'Other', + conditionalQuestions: [ + { + questionId: 'safepeopleotherindividualsroleotherdetails', + input: { type: 'textareaInput' }, + question: 'If other, please specify', + }, + ], + }, + ], + }, + questionId: 'safepeopleotherindividualsrole', + guidance: + 'A role is a function that the applicant plays. It might include role types and accreditation for those that are accessing the secure data and those that are not but would see cleared outputs from the project. \r\n (i.e. project lead, deputy lead, accrediter, researcher, peer reviewer)', + question: 'Role', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + question: 'Will this person access the data requested?', + questionId: 'safepeopleotherindividualsaccessdata', + input: { + options: [ + { value: 'Yes', text: 'Yes' }, + { text: 'No', value: 'No' }, + ], + label: 'Will this person access the data requested?', + type: 'radioOptionsInput', + }, + lockedQuestion: 1, + defaultQuestion: 1, + }, + { + questionId: 'safepeopleotherindividualsaccreditedresearcher', + input: { + options: [ + { + conditionalQuestions: [ + { + question: 'If yes, please provide details', + questionId: 'safepeopleotherindividualsaccreditedresearcherdetails', + input: { type: 'textareaInput' }, + }, + ], + value: 'Yes', + text: 'Yes', + }, + { value: 'No', text: 'No' }, + ], + type: 'radioOptionsInput', + label: 'Is this person an accredited researcher under the Digital Economy Act 2017?', + }, + question: 'Is this person an accredited researcher under the Digital Economy Act 2017?', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + input: { + label: 'Has this person undertaken professional training or education on the topic of Information Governance?', + type: 'radioOptionsInput', + options: [ + { + text: 'Yes', + value: 'Yes', + conditionalQuestions: [ + { + input: { type: 'textareaInput' }, + questionId: 'safepeopleotherindividualstraininginformationgovernancerecent', + question: 'Please provide full details regarding the most recent training', + }, + ], + }, + { + conditionalQuestions: [ + { + questionId: 'safepeopleotherindividualstraininginformationgovernanceintention', + input: { type: 'textareaInput' }, + question: 'Please provide any details of plans to attend training, if applicable', + }, + ], + value: 'No', + text: 'No', + }, + ], + }, + questionId: 'safepeopleotherindividualstraininginformationgovernance', + question: 'Has this person undertaken professional training or education on the topic of Information Governance?', + lockedQuestion: 1, + defaultQuestion: 0, + }, + { + question: "Please provide evidence of this person's expertise and experience relevant to delivering the project", + input: { type: 'textareaInput' }, + questionId: 'safepeopleotherindividualsexperience', + lockedQuestion: 1, + defaultQuestion: 1, + }, + ], + }, + ], + }, +}; + +export const questionStatusMissingEntriesStub = { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, +}; + +export const dataRequestSchemaDraftStub = { + _id: { $oid: '619baf36eb175aaacd592bbb' }, + version: 1, + dataSetId: '', + publisher: 'ALLIANCE > SAIL', + formType: '5 safe', + countOfChanges: 1, + status: 'draft', + isCloneable: true, + guidance: { + safepeopleprimaryapplicantfullname: 'Guidance for safepeopleprimaryapplicantfullname', + safepeopleprimaryapplicantjobtitle: 'Guidance for safepeopleprimaryapplicantjobtitle', + safepeopleprimaryapplicanttelephone: 'Guidance for safepeopleprimaryapplicanttelephone', + }, + questionStatus: { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleprimaryapplicantaccessdata: 0, + safepeopleprimaryapplicantaccreditedresearcher: 0, + safepeopleprimaryapplicanttraininginformationgovernance: 0, + safepeopleprimaryapplicantorganisationname: 0, + safepeopleprimaryapplicantorganisationdatasecurityprotectionkit: 0, + safepeopleprimaryapplicantorganisationdatacontroller: 0, + safepeopleotherindividualsfullname: 0, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 0, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 0, + 'add-safepeople-otherindividuals': 0, + safeprojectaboutthisapplicationtype: 0, + safeprojectprojectdetailstitle: 0, + safeprojectprojectdetailstype: 0, + safeprojectprojectdetailsneworexisting: 0, + safeprojectprojectdetailslaysummary: 0, + 'safeproject-projectdetails-startdate': 0, + 'safeproject-projectdetails-enddate': 0, + safeprojectprojectdetailsaimsobjectivesrationale: 0, + safeprojectprojectdetailsdatausageforobjectives: 0, + safeprojectprojectdetailspublicbenefitimpact: 0, + safeprojectprojectdetailspublicpatientinvolvementengagement: 0, + safeprojectprojectdetailsindividualprivacyriskmitigation: 0, + safeprojectprojectdetailsgroupindividualdisavantageavoidance: 0, + safeprojectprojectdetailsresearchprojectsummarykeywords: 0, + safeprojectprojectdetailsresearchprojectfuturedatabasebenefit: 0, + safeprojectfunderinformationprojecthasfunder: 0, + safeprojectsponsorinformationprojecthassponsor: 0, + safeprojectdeclarationofinteresthascommercialinterest: 0, + safedatadatafieldsdatarequiredjustification: 0, + safedatadatafieldsfileuploaded: 0, + safedatadatafieldsinclusionexclusioncriteria: 0, + safedatadatafieldsdatarefreshrequired: 0, + safedataotherdatasetslinkadditionaldatasets: 0, + safedataotherdatasetsrisksmitigations: 0, + safedatalawfulbasisgdprarticle6basis: 0, + safedatalawfulbasisgdprarticle6basisjustification: 0, + safedatalawfulbasisgdprarticle9conditions: 0, + safedatalawfulbasisgdprarticle9basisjustification: 0, + safedataconfidentialityavenuelegalbasisconfidentialinformation: 0, + safedataethicsapprovalinsightresearch: 0, + safedataethicsapprovalapprovalobtained: 0, + safedatastorageandprocessingaccessmethod: 0, + safedatadataflowdatatransferedoutsideuk: 0, + safedatadataflowregionsdataprocessed: 0, + safedatadataflowdetailedinformation: 0, + safedatadataflowdiagramenclosed: 0, + safeoutputsoutputsdisseminationplansproposalfindings: 0, + safeoutputsoutputsdisseminationplansmilestones: 0, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, + 'safeoutputs-dataretention-retaindatadate': 0, + safeoutputsdataretentionretaindatadatereason: 0, + safeoutputsdataretentionretaindataextensionpermissions: 0, + safeprojectintellectualpropertydevelopmentnewproductprocess: null, + }, + createdAt: { $date: { $numberLong: '1637592886679' } }, + updatedAt: { $date: { $numberLong: '1637601834397' } }, + __v: 0, +}; diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js new file mode 100644 index 00000000..5785e6da --- /dev/null +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -0,0 +1,63 @@ +import sinon from 'sinon'; +import DataRequestRepository from '../../datarequest/datarequest.repository'; +import QuestionBankService from '../questionbank.service'; +import GlobalService from '../../global/global.service'; +import { globalStub, publisherStub, questionStatusMissingEntriesStub, dataRequestSchemaDraftStub } from '../__mocks__/questionbank'; +import PublisherService from '../../publisher/publisher.service'; +import QuestionBankRepository from '../questionbank.repository'; +import PublisherRepository from '../../publisher/publisher.repository'; + +describe('Question Bank Service', function () { + it('When no entry exists in the schema', async function () { + //dataSchemaModel + const dataRequestRepository = new DataRequestRepository(); + const dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); + const dataRequestRepositoryStub2 = sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); + + const globalService = new GlobalService(); + const globalServiceStub = sinon.stub(globalService, 'getGlobal').returns(globalStub); + + const publisherRepository = new PublisherRepository(); + const publisherRepositoryStub = sinon.stub(publisherRepository, 'getPublisher').returns({}); + + const publisherService = new PublisherService(publisherRepository); + + const questionBankRepository = new QuestionBankRepository(); + + const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + + const result = await questionBankService.getQuestionBankInfo(publisherStub._id); + + //Il risultato + expect(result.questionStatus).toEqual(questionStatusMissingEntriesStub); + expect(result.guidance).toEqual({}); + expect(result.countOfChanges).toEqual(0); + expect(result.masterSchema).toEqual(globalStub.masterSchema); + }); + + it('When entry exists in draft status', async function () { + //dataSchemaModel + const dataRequestRepository = new DataRequestRepository(); + const dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([dataRequestSchemaDraftStub]); + const dataRequestRepositoryStub2 = sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); + + const globalService = new GlobalService(); + const globalServiceStub = sinon.stub(globalService, 'getGlobal').returns(globalStub); + + const publisherRepository = new PublisherRepository(); + const publisherRepositoryStub = sinon.stub(publisherRepository, 'getPublisher').returns({}); + + const publisherService = new PublisherService(publisherRepository); + + const questionBankRepository = new QuestionBankRepository(); + + const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + + const result = await questionBankService.getQuestionBankInfo(publisherStub._id); + + expect(result.questionStatus).toEqual(dataRequestSchemaDraftStub.questionStatus); + expect(result.guidance).toEqual(dataRequestSchemaDraftStub.guidance); + expect(result.countOfChanges).toEqual(dataRequestSchemaDraftStub.countOfChanges); + expect(result.masterSchema).toEqual(globalStub.masterSchema); + }); +}); diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js index 49b21029..0a346e63 100644 --- a/src/resources/questionbank/dependency.js +++ b/src/resources/questionbank/dependency.js @@ -2,6 +2,7 @@ import QuestionbankRepository from './questionbank.repository'; import QuestionbankService from './questionbank.service'; import { publisherService } from '../publisher/dependency'; import { globalService } from '../global/dependency'; +import dataRequestRepository from '../datarequest/datarequest.repository'; export const questionbankRepository = new QuestionbankRepository(); -export const questionbankService = new QuestionbankService(questionbankRepository, publisherService, globalService); +export const questionbankService = new QuestionbankService(questionbankRepository, publisherService, globalService, dataRequestRepository); diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 7f4a0e02..5fdaea67 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,11 +1,11 @@ -import { isNull, isEmpty } from 'lodash'; -import { DataRequestSchemaModel } from '../datarequest/schema/datarequest.schemas.model'; +import { isEmpty } from 'lodash'; export default class QuestionbankService { - constructor(questionbankRepository, publisherService, globalService) { + constructor(questionbankRepository, publisherService, globalService, dataRequestRepository) { this.questionbankRepository = questionbankRepository; this.publisherService = publisherService; this.globalService = globalService; + this.dataRequestRepository = dataRequestRepository; } getQuestionbank(id, query = {}, options = {}) { @@ -25,7 +25,7 @@ export default class QuestionbankService { const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); const masterSchema = global.masterSchema; - let dataRequestSchemas = await DataRequestSchemaModel.find({ publisher: publisher.name }).sort({ version: -1 }); + let dataRequestSchemas = await this.dataRequestRepository.getApplicationFormSchemas(publisher); if (isEmpty(dataRequestSchemas)) { let questionStatus = {}; @@ -36,20 +36,22 @@ export default class QuestionbankService { }); }); - const newSchema = new DataRequestSchemaModel({ + const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus, - }); + guidance: {}, + countOfChanges: 0, + }; - await DataRequestSchemaModel.create(newSchema); + await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, - questionStatus: newSchema.questionStatus, - guidance: newSchema.guidance, - countOfChanges: newSchema.countOfChanges, + questionStatus, + guidance: {}, + countOfChanges: 0, }; } @@ -69,7 +71,7 @@ export default class QuestionbankService { }); if (newQuestionsAdded) - await DataRequestSchemaModel.findOneAndUpdate({ _id: latestSchemaVersion._id }, { $set: { questionStatus: newQuestionStatus } }); + await this.dataRequestRepository.updateApplicationFormSchemaById(latestSchemaVersion._id, { questionStatus: newQuestionStatus }); return { masterSchema, @@ -92,16 +94,16 @@ export default class QuestionbankService { }); }); - const newSchema = new DataRequestSchemaModel({ + const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus: newQuestionStatus, guidance: latestSchemaVersion.guidance, version: latestSchemaVersion.version + 1, - }); + }; - await DataRequestSchemaModel.create(newSchema); + await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, @@ -130,16 +132,16 @@ export default class QuestionbankService { }); }); - const newSchema = new DataRequestSchemaModel({ + const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus, guidance, version: latestSchemaVersion.version + 1, - }); + }; - await DataRequestSchemaModel.create(newSchema); + await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, From 1b35841c1ac2193670c5b730e9419839fd392884 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 24 Nov 2021 13:14:41 +0000 Subject: [PATCH 004/389] Added ability to update the data request modal content --- .../publisher/publisher.controller.js | 32 +++++++++++++++++++ src/resources/publisher/publisher.model.js | 4 +++ .../publisher/publisher.repository.js | 4 +++ src/resources/publisher/publisher.route.js | 4 +++ src/resources/publisher/publisher.service.js | 11 +++++++ 5 files changed, 55 insertions(+) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 1cb25865..b41cf6d2 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -4,6 +4,7 @@ import constants from '../utilities/constants.util'; import teamController from '../team/team.controller'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; +import { TeamModel } from '../team/team.model'; const logCategory = 'Publisher'; @@ -161,4 +162,35 @@ export default class PublisherController extends Controller { }); } } + + async updateDataRequestModalContent(req, res) { + try { + const { _id: requestingUserId } = req.user; + + const team = await TeamModel.findOne({ _id: req.params.id }) + .populate({ + path: 'users', + populate: { + path: 'additionalInfo', + select: 'organisation bio showOrganisation showBio', + }, + }) + .lean(); + + // Check that user is a manager + const authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserId); + if (!authorised) { + return res.status(401).json({ success: false }); + } + + await this.publisherService.updateDataRequestModalContent(req.params.id, req.user.id, req.body.content).then(() => { + 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', + }); + } + } } diff --git a/src/resources/publisher/publisher.model.js b/src/resources/publisher/publisher.model.js index 65740c96..20ff7fd5 100644 --- a/src/resources/publisher/publisher.model.js +++ b/src/resources/publisher/publisher.model.js @@ -21,6 +21,10 @@ const PublisherSchema = new Schema( body: String, footer: String, }, + dataRequestModalContentUpdatedOn: Date, + dataRequestModalContentUpdatedBy: Number, + applicationFormUpdatedOn: Date, + applicationFormUpdatedBy: Number, workflowEnabled: { type: Boolean, default: false, diff --git a/src/resources/publisher/publisher.repository.js b/src/resources/publisher/publisher.repository.js index ffdb8bfc..88c33a13 100644 --- a/src/resources/publisher/publisher.repository.js +++ b/src/resources/publisher/publisher.repository.js @@ -62,4 +62,8 @@ export default class PublisherRepository extends Repository { ]) .lean(); } + + async updatePublisher(query, options) { + return this.updateByQuery(query, options); + } } diff --git a/src/resources/publisher/publisher.route.js b/src/resources/publisher/publisher.route.js index e8a1e392..6817ef48 100644 --- a/src/resources/publisher/publisher.route.js +++ b/src/resources/publisher/publisher.route.js @@ -54,4 +54,8 @@ router.get( (req, res) => publisherController.getPublisherWorkflows(req, res) ); +router.patch('/dataRequestModalContent/:id', passport.authenticate('jwt'), (req, res) => + publisherController.updateDataRequestModalContent(req, res) +); + module.exports = router; diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index f56f5a5c..60b06de1 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -94,4 +94,15 @@ export default class PublisherService { return filteredApplications; } + + async updateDataRequestModalContent(publisherId, requestingUserId, content) { + await this.publisherRepository.updatePublisher( + { _id: publisherId }, + { + dataRequestModalContentUpdatedOn: Date.now(), + dataRequestModalContentUpdatedBy: requestingUserId, + dataRequestModalContent: { header: '', body: content, footer: '' }, + } + ); + } } From 11d602cd41be7822a6b6adc5e65c6e57f07fabda Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Wed, 24 Nov 2021 14:03:15 +0000 Subject: [PATCH 005/389] Fix dependency bug and data request repository --- src/resources/datarequest/datarequest.repository.js | 2 +- src/resources/questionbank/dependency.js | 3 ++- src/resources/questionbank/masterSchema.json | 4 +++- src/resources/questionbank/questionbank.service.js | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/resources/datarequest/datarequest.repository.js b/src/resources/datarequest/datarequest.repository.js index 9a88fe78..0d870c91 100644 --- a/src/resources/datarequest/datarequest.repository.js +++ b/src/resources/datarequest/datarequest.repository.js @@ -175,7 +175,7 @@ export default class DataRequestRepository extends Repository { } updateApplicationFormSchemaById(id, data, options = {}) { - DataRequestSchemaModel.findByIdAndUpdate(id, data, { ...options }); + return DataRequestSchemaModel.findByIdAndUpdate(id, data, { ...options }); } getDatasetsForApplicationByIds(datasetIds) { diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js index 0a346e63..67d88ac3 100644 --- a/src/resources/questionbank/dependency.js +++ b/src/resources/questionbank/dependency.js @@ -2,7 +2,8 @@ import QuestionbankRepository from './questionbank.repository'; import QuestionbankService from './questionbank.service'; import { publisherService } from '../publisher/dependency'; import { globalService } from '../global/dependency'; -import dataRequestRepository from '../datarequest/datarequest.repository'; +import DataRequestRepository from '../datarequest/datarequest.repository'; export const questionbankRepository = new QuestionbankRepository(); +export const dataRequestRepository = new DataRequestRepository(); export const questionbankService = new QuestionbankService(questionbankRepository, publisherService, globalService, dataRequestRepository); diff --git a/src/resources/questionbank/masterSchema.json b/src/resources/questionbank/masterSchema.json index f637b004..0e09f4f2 100644 --- a/src/resources/questionbank/masterSchema.json +++ b/src/resources/questionbank/masterSchema.json @@ -1551,7 +1551,9 @@ { "input": { "type": "textareaInput" }, "questionId": "safeprojectintellectualpropertydevelopmentnewproductprocess", - "question": "Please indicate if the research could lead to the development of a new product/process or the generation of intellectual property." + "question": "Please indicate if the research could lead to the development of a new product/process or the generation of intellectual property.", + "lockedQuestion": 1, + "defaultQuestion": 0 } ] }, diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 5fdaea67..7f031669 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -101,6 +101,7 @@ export default class QuestionbankService { questionStatus: newQuestionStatus, guidance: latestSchemaVersion.guidance, version: latestSchemaVersion.version + 1, + countOfChanges: 0, }; await this.dataRequestRepository.createApplicationFormSchema(newSchema); From 9f9c561d523d8a0e5b0b85ff0f2b6c3eac0b2c9f Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Wed, 24 Nov 2021 17:03:34 +0000 Subject: [PATCH 006/389] Add test data --- .../activeSchemaCreatedThroughForm.js | 56 +++ .../activeSchemaNotCreatedThroughForm.js | 331 ++++++++++++++++++ .../draftSchemaCreatedThroughForm.js | 83 +++++ .../questionbank/__mocks__/noSchemaExists.js | 18 + .../questionbank/__mocks__/questionbank.js | 104 +----- .../__tests__/questionbank.service.test.js | 94 ++--- .../questionbank/questionbank.service.js | 13 +- 7 files changed, 551 insertions(+), 148 deletions(-) create mode 100644 src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js create mode 100644 src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js create mode 100644 src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js create mode 100644 src/resources/questionbank/__mocks__/noSchemaExists.js diff --git a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js new file mode 100644 index 00000000..f5e53dda --- /dev/null +++ b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js @@ -0,0 +1,56 @@ +export const dataRequestSchema = { + _id: { $oid: '619e3176135ebd6de948f3dc' }, + version: { $numberInt: '3' }, + dataSetId: '', + publisher: 'ALLIANCE > SAIL', + formType: '5 safe', + countOfChanges: 0, + status: 'active', + isCloneable: true, + questionStatus: { + safedatadataflowdiagramenclosed: 1, + safeoutputsoutputsdisseminationplansproposalfindings: 0, + safeoutputsoutputsdisseminationplansmilestones: 0, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, + 'safeoutputs-dataretention-retaindatadate': 0, + safeoutputsdataretentionretaindatadatereason: 1, + safeoutputsdataretentionretaindataextensionpermissions: 0, + }, + createdAt: { $date: { $numberLong: '1637757303647' } }, + updatedAt: { $date: { $numberLong: '1637757380701' } }, + __v: 0, + guidance: { + safedatadataflowdiagramenclosed: 'This is the guidance for safedatadataflowdiagramenclosed', + 'safeoutputs-dataretention-retaindatadate': 'This is the guidance for safeoutputs-dataretention-retaindatadate', + }, +}; + +export const expectedQuestionStatus = { + safedatadataflowdiagramenclosed: 1, + safeoutputsoutputsdisseminationplansproposalfindings: 0, + safeoutputsoutputsdisseminationplansmilestones: 0, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, + 'safeoutputs-dataretention-retaindatadate': 0, + safeoutputsdataretentionretaindatadatereason: 1, + safeoutputsdataretentionretaindataextensionpermissions: 0, + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, +}; + +export const expectedGuidance = { + safedatadataflowdiagramenclosed: 'This is the guidance for safedatadataflowdiagramenclosed', + 'safeoutputs-dataretention-retaindatadate': 'This is the guidance for safeoutputs-dataretention-retaindatadate', +}; + +export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js new file mode 100644 index 00000000..ceac4331 --- /dev/null +++ b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js @@ -0,0 +1,331 @@ +export const dataRequestSchema = { + _id: { $oid: '5fbabae775c2095bdbdc1533' }, + version: { $numberInt: '2' }, + publisher: 'ALLIANCE > SAIL', + id: { $numberLong: '45651390300502031' }, + status: 'active', + jsonSchema: { + pages: [ + { + description: + 'Who is going to be accessing the data?\n\nSafe People should have the right motivations for accessing research data and understand the legal and ethical considerations when using data that may be sensitive or confidential. Safe People should also have sufficient skills, knowledge and experience to work with the data effectively. Researchers may need to undergo specific training or accreditation before accessing certain data or research environments and demonstrate that they are part of a bona fide research organisation.\n\nThe purpose of this section is to ensure that:\n- details of people who will be accessing the data and the people who are responsible for completing the application are identified\n- any individual or organisation that intends to access the data requested is identified\n- all identified individuals have the necessary accreditation and/or expertise to work with the data effectively.', + title: 'Safe people', + active: true, + pageId: 'safepeople', + }, + { description: 'Description', title: 'Safe project', active: false, pageId: 'safeproject' }, + { description: 'Description', title: 'Safe data', active: false, pageId: 'safedata' }, + { pageId: 'safesettings', active: false, title: 'Safe settings', description: 'Description' }, + { title: 'Safe outputs', description: 'Description', pageId: 'safeoutputs', active: false }, + ], + formPanels: [ + { panelId: 'primaryapplicant', index: { $numberDouble: '1.0' }, pageId: 'safepeople' }, + { index: { $numberDouble: '2.0' }, pageId: 'safepeople', panelId: 'safepeople-otherindividuals' }, + { index: { $numberDouble: '3.0' }, pageId: 'safeproject', panelId: 'safeproject-aboutthisapplication' }, + { index: { $numberDouble: '4.0' }, pageId: 'safeproject', panelId: 'safeproject-projectdetails' }, + { panelId: 'safeproject-funderinformation', index: { $numberDouble: '5.0' }, pageId: 'safeproject' }, + { index: { $numberDouble: '6.0' }, pageId: 'safeproject', panelId: 'safeproject-sponsorinformation' }, + { pageId: 'safeproject', index: { $numberDouble: '7.0' }, panelId: 'safeproject-declarationofinterest' }, + { index: { $numberDouble: '8.0' }, pageId: 'safeproject', panelId: 'safeproject-intellectualproperty' }, + { panelId: 'safedata-datafields', pageId: 'safedata', index: { $numberDouble: '9.0' } }, + { index: { $numberDouble: '10.0' }, pageId: 'safedata', panelId: 'safedata-otherdatasetsintentiontolinkdata' }, + { panelId: 'safedata-lawfulbasis', pageId: 'safedata', index: { $numberDouble: '11.0' } }, + { panelId: 'safedata-confidentialityavenue', pageId: 'safedata', index: { $numberDouble: '12.0' } }, + { pageId: 'safedata', index: { $numberDouble: '13.0' }, panelId: 'safedata-ethicalapproval' }, + { index: { $numberDouble: '14.0' }, pageId: 'safesettings', panelId: 'safesettings-storageandprocessing' }, + { pageId: 'safesettings', index: { $numberDouble: '15.0' }, panelId: 'safesettings-dataflow' }, + { panelId: 'safeoutputs-outputsdisseminationplans', index: { $numberDouble: '16.0' }, pageId: 'safeoutputs' }, + { pageId: 'safeoutputs', index: { $numberDouble: '17.0' }, panelId: 'safeoutputs-retention' }, + ], + questionPanels: [ + { + questionSets: [{ questionSetId: 'primaryapplicant', index: { $numberDouble: '1.0' } }], + pageId: 'safepeople', + questionPanelHeaderText: 'TODO: We need a description for this panel', + panelHeader: + "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", + panelId: 'primaryapplicant', + navHeader: 'Primary applicant', + }, + { + questionSets: [ + { index: { $numberDouble: '1.0' }, questionSetId: 'safepeople-otherindividuals' }, + { index: { $numberDouble: '100.0' }, questionSetId: 'add-safepeople-otherindividuals' }, + ], + pageId: 'safepeople', + questionPanelHeaderText: 'TODO: We need a description for this panel', + panelHeader: + "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", + panelId: 'safepeople-otherindividuals', + navHeader: 'Other individuals', + }, + { + navHeader: 'About this application', + panelHeader: '', + panelId: 'safeproject-aboutthisapplication', + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ questionSetId: 'safeproject-aboutthisapplication', index: { $numberDouble: '1.0' } }], + pageId: 'safeproject', + }, + { + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safeproject', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safeproject-projectdetails' }], + navHeader: 'Project details', + panelId: 'safeproject-projectdetails', + panelHeader: '', + }, + { + navHeader: 'Funder information', + panelHeader: + "A funder is the organisation or body providing the financial resource to make the project possible, and may be different to the organisation detailed in the Safe people section. Please provide details of the main funder organisations supporting this project.\n\nPlease use the file upload function if you're not able to add all funders via the form.", + panelId: 'safeproject-funderinformation', + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ questionSetId: 'safeproject-funderinformation', index: { $numberDouble: '1.0' } }], + pageId: 'safeproject', + }, + { + navHeader: 'Sponsor information', + panelId: 'safeproject-sponsorinformation', + panelHeader: "Please use the file upload function if you're not able to add all sponsors via the form.", + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safeproject', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safeproject-sponsorinformation' }], + }, + { + navHeader: 'Declaration of interest', + panelHeader: '', + panelId: 'safeproject-declarationofinterest', + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ questionSetId: 'safeproject-declarationofinterest', index: { $numberDouble: '1.0' } }], + pageId: 'safeproject', + }, + { + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safeproject', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safeproject-intellectualproperty' }], + navHeader: 'Intellectual property', + panelId: 'safeproject-intellectualproperty', + panelHeader: '', + }, + { + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ questionSetId: 'safedata-datafields', index: { $numberDouble: '1.0' } }], + pageId: 'safedata', + navHeader: 'Data fields', + panelHeader: '', + panelId: 'safedata-datafields', + }, + { + navHeader: 'Other datasets - Intention to link data', + panelId: 'safedata-otherdatasetsintentiontolinkdata', + panelHeader: '', + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safedata', + questionSets: [{ questionSetId: 'safedata-otherdatasetsintentiontolinkdata', index: { $numberDouble: '1.0' } }], + }, + { + navHeader: 'Lawful basis', + panelId: 'safedata-lawfulbasis', + panelHeader: '', + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safedata', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safedata-lawfulbasis' }], + }, + { + navHeader: 'Confidentiality avenue', + panelId: 'safedata-confidentialityavenue', + panelHeader: '', + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safedata', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safedata-confidentialityavenue' }], + }, + { + navHeader: 'Ethical approval', + panelId: 'safedata-ethicalapproval', + panelHeader: + 'This section details the research and ethics approval which you have obtained or sought for your project, or otherwise provides evidence as to why such approval is not necessary. For instance, for data analysis done on data deemed anonymous, no ethical approval is explicitly needed for said analyses. \n\nWhere such approval is not in place, it is important that you demonstrate why this is the case and provide assurances if approval is pending. If you need advice on whether ethics approval is necessary, you should approach your local ethics services in the first instance. Information about UK research ethics committees and ethical opinions can be found on the Health Research Authority (HRA) website.', + questionPanelHeaderText: 'TODO: We need a description for this panel', + pageId: 'safedata', + questionSets: [{ questionSetId: 'safedata-ethicalapproval', index: { $numberDouble: '1.0' } }], + }, + { + panelHeader: + "This section details in what way the proposal aims to store and use data, and controls in place to minimise risks associated with this storage and use. If you have indicated that your proposal seeks to store and use data exclusively through a recognised trusted research environment, then you do not need to complete this section.\n In relation to personal data, means any operation or set of operations which is performed on personal data or on sets of personal data (whether or not by automated means, such as collection, recording, organisation, structuring, storage, alteration, retrieval, consultation, use, disclosure, dissemination, restriction, erasure or destruction).\n \n All Locations where processing will be undertaken, for the avoidance of doubt storage is considered processing. For each separate organisation processing data which is not fully anonymous a separate partner organisation form must also be completed.\n \n Processing, in relation to information or data means obtaining, recording or holding the information or data or carrying out any operation or set of operations on the information or data, including—\n a) organisation, adaptation or alteration of the information or data,\n b) retrieval, consultation or use of the information or data,\n c) disclosure of the information or data by transmission,\n dissemination or otherwise making available, or\n d) alignment, combination, blocking, erasure or destruction of the information or data.\n\nPlease note that where an applicant has explicit consent from participants (such as in cohort studies) to hold linked health records, and therefore request extraction of data held by SAIL back to their own research setting, rather than be accessed solely in the remote desktop environment SAIL providers, a user may apply in these circumstances to SAIL for such extraction, with justification provided through the means of their consent documentation and ethical approval to hold such data outside of our secure setting.\n\nPlease use the file upload function if you're not able to add all organisations via the form.", + panelId: 'safesettings-storageandprocessing', + navHeader: 'Storage and processing', + questionSets: [{ questionSetId: 'safesettings-storageandprocessing', index: { $numberDouble: '1.0' } }], + pageId: 'safesettings', + questionPanelHeaderText: 'TODO: We need a description for this panel', + }, + { + navHeader: 'Dataflow', + panelHeader: '', + panelId: 'safesettings-dataflow', + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ index: { $numberDouble: '1.0' }, questionSetId: 'safesettings-dataflow' }], + pageId: 'safesettings', + }, + { + panelHeader: '', + panelId: 'safeoutputs-outputsdisseminationplans', + navHeader: 'Outputs dissemination plans', + questionSets: [{ questionSetId: 'safeoutputs-outputsdisseminationplans', index: { $numberDouble: '1.0' } }], + pageId: 'safeoutputs', + questionPanelHeaderText: 'TODO: We need a description for this panel', + }, + { + questionPanelHeaderText: 'TODO: We need a description for this panel', + questionSets: [{ questionSetId: 'safeoutputs-retention', index: { $numberDouble: '1.0' } }], + pageId: 'safeoutputs', + navHeader: 'Retention', + panelHeader: '', + panelId: 'safeoutputs-retention', + }, + ], + questionSets: [ + { + questionSetId: 'safesettings-dataflow', + questionSetHeader: 'Dataflow', + questions: [ + { + validations: [{ message: 'Please select an option', type: 'isLength', params: [{ $numberDouble: '1.0' }] }], + question: 'Will the data be transferred outside of the United Kingdom?', + questionId: 'safedatadataflowdatatransferedoutsideuk', + input: { + options: [ + { + text: 'Yes', + conditionalQuestions: [ + { + input: { type: 'textareaInput' }, + questionId: 'safedatadataflowdatatransferedoutsideukdetails', + question: 'If yes, please provide more details', + }, + ], + value: 'Yes', + }, + { text: 'No', value: 'No' }, + ], + type: 'radioOptionsInput', + required: true, + label: 'Will the data be transferred outside of the United Kingdom?', + }, + }, + { + input: { + options: [ + { text: 'England/Wales', value: 'England/Wales' }, + { value: 'United Kingdom', text: 'United Kingdom' }, + { value: 'European Economic Area', text: 'European Economic Area' }, + { text: 'Other', value: 'Other' }, + ], + type: 'checkboxOptionsInput', + label: 'Please specify the regions where data will be processed.', + }, + question: 'Please specify the regions where data will be processed.', + questionId: 'safedatadataflowregionsdataprocessed', + }, + { + question: 'Please provide detailed information on data flows', + questionId: 'safedatadataflowdetailedinformation', + input: { type: 'textareaInput' }, + }, + { + question: 'Please include a data flow diagram for the requested data and any additional datasets intended to be linked.', + questionId: 'safedatadataflowdiagramenclosed', + input: { + label: 'Please include a data flow diagram for the requested data and any additional datasets intended to be linked.', + options: [{ text: 'I have enclosed a copy of the dataflow', value: 'I have enclosed a copy of the dataflow' }], + type: 'checkboxOptionsInput', + }, + }, + ], + }, + { + questionSetHeader: 'Outputs dissemination plans', + questionSetId: 'safeoutputs-outputsdisseminationplans', + questions: [ + { + validations: [{ params: [{ $numberDouble: '1.0' }], message: 'Please enter a value', type: 'isLength' }], + question: 'How will proposal findings be disseminated, to what audience and in what format?', + questionId: 'safeoutputsoutputsdisseminationplansproposalfindings', + input: { required: true, type: 'textareaInput' }, + }, + { + question: 'Please include any milestones for outputs dissemination.', + questionId: 'safeoutputsoutputsdisseminationplansmilestones', + input: { type: 'textareaInput', required: true }, + validations: [{ type: 'isLength', message: 'Please enter a value', params: [{ $numberDouble: '1.0' }] }], + }, + { + questionId: 'safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy', + question: + 'What steps will be taken to ensure that individuals cannot be identified? Please describe what disclosure control policy will be applied.', + input: { type: 'textareaInput', required: true }, + validations: [{ params: [{ $numberDouble: '1.0' }], message: 'Please enter a value', type: 'isLength' }], + }, + ], + }, + { + questions: [ + { + validations: [{ params: [{ $numberDouble: '1.0' }], type: 'isLength', message: 'Please enter a value' }], + input: { required: true, type: 'datePickerCustom' }, + questionId: 'safeoutputs-dataretention-retaindatadate', + question: 'Please state the date until which you will retain the data', + }, + { + questionId: 'safeoutputsdataretentionretaindatadatereason', + question: 'Please indicate the reason for this date', + input: { type: 'textareaInput' }, + }, + { + questionId: 'safeoutputsdataretentionretaindataextensionpermissions', + question: + 'Please provide details of any permissions that will need to apply for an extension to during this period in order to retain a legal basis to hold the data (e.g. section 251)', + input: { type: 'textareaInput' }, + }, + ], + questionSetId: 'safeoutputs-retention', + questionSetHeader: 'Retention', + }, + ], + }, + createdAt: { $date: { $numberLong: '1606034106629' } }, + updatedAt: { $date: { $numberLong: '1616431822002' } }, + __v: { $numberInt: '0' }, + formType: '5 safe', + dataSetId: '', + isCloneable: true, +}; + +//Combination of the questions in the active schema and the questions in the master schema +export const expectedQuestionStatus = { + safedatadataflowdatatransferedoutsideuk: 1, + safedatadataflowregionsdataprocessed: 1, + safedatadataflowdetailedinformation: 1, + safedatadataflowdiagramenclosed: 1, + safeoutputsoutputsdisseminationplansproposalfindings: 1, + safeoutputsoutputsdisseminationplansmilestones: 1, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 1, + 'safeoutputs-dataretention-retaindatadate': 1, + safeoutputsdataretentionretaindatadatereason: 1, + safeoutputsdataretentionretaindataextensionpermissions: 1, + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, +}; + +export const expectedGuidance = {}; +export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js new file mode 100644 index 00000000..d45f25d5 --- /dev/null +++ b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js @@ -0,0 +1,83 @@ +export const dataRequestSchema = { + _id: { $oid: '619baf36eb175aaacd592bbb' }, + version: 1, + dataSetId: '', + publisher: 'ALLIANCE > SAIL', + formType: '5 safe', + countOfChanges: 1, + status: 'draft', + isCloneable: true, + guidance: { + safepeopleprimaryapplicantfullname: 'Guidance for safepeopleprimaryapplicantfullname', + safepeopleprimaryapplicantjobtitle: 'Guidance for safepeopleprimaryapplicantjobtitle', + safepeopleprimaryapplicanttelephone: 'Guidance for safepeopleprimaryapplicanttelephone', + }, + questionStatus: { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleprimaryapplicantaccessdata: 0, + safepeopleprimaryapplicantaccreditedresearcher: 0, + safepeopleprimaryapplicanttraininginformationgovernance: 0, + safepeopleprimaryapplicantorganisationname: 0, + safepeopleprimaryapplicantorganisationdatasecurityprotectionkit: 0, + safepeopleprimaryapplicantorganisationdatacontroller: 0, + safepeopleotherindividualsfullname: 0, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 0, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 0, + 'add-safepeople-otherindividuals': 0, + safeprojectaboutthisapplicationtype: 0, + safeprojectprojectdetailstitle: 0, + safeprojectprojectdetailstype: 0, + safeprojectprojectdetailsneworexisting: 0, + safeprojectprojectdetailslaysummary: 0, + 'safeproject-projectdetails-startdate': 0, + 'safeproject-projectdetails-enddate': 0, + safeprojectprojectdetailsaimsobjectivesrationale: 0, + safeprojectprojectdetailsdatausageforobjectives: 0, + safeprojectprojectdetailspublicbenefitimpact: 0, + safeprojectprojectdetailspublicpatientinvolvementengagement: 0, + safeprojectprojectdetailsindividualprivacyriskmitigation: 0, + safeprojectprojectdetailsgroupindividualdisavantageavoidance: 0, + safeprojectprojectdetailsresearchprojectsummarykeywords: 0, + safeprojectprojectdetailsresearchprojectfuturedatabasebenefit: 0, + safeprojectfunderinformationprojecthasfunder: 0, + safeprojectsponsorinformationprojecthassponsor: 0, + safeprojectdeclarationofinteresthascommercialinterest: 0, + safedatadatafieldsdatarequiredjustification: 0, + safedatadatafieldsfileuploaded: 0, + safedatadatafieldsinclusionexclusioncriteria: 0, + safedatadatafieldsdatarefreshrequired: 0, + safedataotherdatasetslinkadditionaldatasets: 0, + safedataotherdatasetsrisksmitigations: 0, + safedatalawfulbasisgdprarticle6basis: 0, + safedatalawfulbasisgdprarticle6basisjustification: 0, + safedatalawfulbasisgdprarticle9conditions: 0, + safedatalawfulbasisgdprarticle9basisjustification: 0, + safedataconfidentialityavenuelegalbasisconfidentialinformation: 0, + safedataethicsapprovalinsightresearch: 0, + safedataethicsapprovalapprovalobtained: 0, + safedatastorageandprocessingaccessmethod: 0, + safedatadataflowdatatransferedoutsideuk: 0, + safedatadataflowregionsdataprocessed: 0, + safedatadataflowdetailedinformation: 0, + safedatadataflowdiagramenclosed: 0, + safeoutputsoutputsdisseminationplansproposalfindings: 0, + safeoutputsoutputsdisseminationplansmilestones: 0, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, + 'safeoutputs-dataretention-retaindatadate': 0, + safeoutputsdataretentionretaindatadatereason: 0, + safeoutputsdataretentionretaindataextensionpermissions: 0, + safeprojectintellectualpropertydevelopmentnewproductprocess: null, + }, + createdAt: { $date: { $numberLong: '1637592886679' } }, + updatedAt: { $date: { $numberLong: '1637601834397' } }, + __v: 0, +}; diff --git a/src/resources/questionbank/__mocks__/noSchemaExists.js b/src/resources/questionbank/__mocks__/noSchemaExists.js new file mode 100644 index 00000000..7140153a --- /dev/null +++ b/src/resources/questionbank/__mocks__/noSchemaExists.js @@ -0,0 +1,18 @@ +export const expectedQuestionStatus = { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, +}; + +export const expectedGuidance = {}; +export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__mocks__/questionbank.js b/src/resources/questionbank/__mocks__/questionbank.js index 692754a8..354f0941 100644 --- a/src/resources/questionbank/__mocks__/questionbank.js +++ b/src/resources/questionbank/__mocks__/questionbank.js @@ -1,4 +1,4 @@ -export const publisherStub = { +export const publisherDocument = { _id: '5f3f98068af2ef61552e1d75', name: 'ALLIANCE > SAIL', active: true, @@ -11,7 +11,7 @@ export const publisherStub = { mdcFolderId: 'c4f50de0-2188-426b-a6cd-6b11a8d6c3cb', }; -export const globalStub = { +export const globalDocument = { masterSchema: { questionSets: [ { @@ -201,103 +201,3 @@ export const globalStub = { ], }, }; - -export const questionStatusMissingEntriesStub = { - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, - safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, -}; - -export const dataRequestSchemaDraftStub = { - _id: { $oid: '619baf36eb175aaacd592bbb' }, - version: 1, - dataSetId: '', - publisher: 'ALLIANCE > SAIL', - formType: '5 safe', - countOfChanges: 1, - status: 'draft', - isCloneable: true, - guidance: { - safepeopleprimaryapplicantfullname: 'Guidance for safepeopleprimaryapplicantfullname', - safepeopleprimaryapplicantjobtitle: 'Guidance for safepeopleprimaryapplicantjobtitle', - safepeopleprimaryapplicanttelephone: 'Guidance for safepeopleprimaryapplicanttelephone', - }, - questionStatus: { - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, - safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleprimaryapplicantaccessdata: 0, - safepeopleprimaryapplicantaccreditedresearcher: 0, - safepeopleprimaryapplicanttraininginformationgovernance: 0, - safepeopleprimaryapplicantorganisationname: 0, - safepeopleprimaryapplicantorganisationdatasecurityprotectionkit: 0, - safepeopleprimaryapplicantorganisationdatacontroller: 0, - safepeopleotherindividualsfullname: 0, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 0, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 0, - 'add-safepeople-otherindividuals': 0, - safeprojectaboutthisapplicationtype: 0, - safeprojectprojectdetailstitle: 0, - safeprojectprojectdetailstype: 0, - safeprojectprojectdetailsneworexisting: 0, - safeprojectprojectdetailslaysummary: 0, - 'safeproject-projectdetails-startdate': 0, - 'safeproject-projectdetails-enddate': 0, - safeprojectprojectdetailsaimsobjectivesrationale: 0, - safeprojectprojectdetailsdatausageforobjectives: 0, - safeprojectprojectdetailspublicbenefitimpact: 0, - safeprojectprojectdetailspublicpatientinvolvementengagement: 0, - safeprojectprojectdetailsindividualprivacyriskmitigation: 0, - safeprojectprojectdetailsgroupindividualdisavantageavoidance: 0, - safeprojectprojectdetailsresearchprojectsummarykeywords: 0, - safeprojectprojectdetailsresearchprojectfuturedatabasebenefit: 0, - safeprojectfunderinformationprojecthasfunder: 0, - safeprojectsponsorinformationprojecthassponsor: 0, - safeprojectdeclarationofinteresthascommercialinterest: 0, - safedatadatafieldsdatarequiredjustification: 0, - safedatadatafieldsfileuploaded: 0, - safedatadatafieldsinclusionexclusioncriteria: 0, - safedatadatafieldsdatarefreshrequired: 0, - safedataotherdatasetslinkadditionaldatasets: 0, - safedataotherdatasetsrisksmitigations: 0, - safedatalawfulbasisgdprarticle6basis: 0, - safedatalawfulbasisgdprarticle6basisjustification: 0, - safedatalawfulbasisgdprarticle9conditions: 0, - safedatalawfulbasisgdprarticle9basisjustification: 0, - safedataconfidentialityavenuelegalbasisconfidentialinformation: 0, - safedataethicsapprovalinsightresearch: 0, - safedataethicsapprovalapprovalobtained: 0, - safedatastorageandprocessingaccessmethod: 0, - safedatadataflowdatatransferedoutsideuk: 0, - safedatadataflowregionsdataprocessed: 0, - safedatadataflowdetailedinformation: 0, - safedatadataflowdiagramenclosed: 0, - safeoutputsoutputsdisseminationplansproposalfindings: 0, - safeoutputsoutputsdisseminationplansmilestones: 0, - safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, - 'safeoutputs-dataretention-retaindatadate': 0, - safeoutputsdataretentionretaindatadatereason: 0, - safeoutputsdataretentionretaindataextensionpermissions: 0, - safeprojectintellectualpropertydevelopmentnewproductprocess: null, - }, - createdAt: { $date: { $numberLong: '1637592886679' } }, - updatedAt: { $date: { $numberLong: '1637601834397' } }, - __v: 0, -}; diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index 5785e6da..c4935de3 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -2,62 +2,78 @@ import sinon from 'sinon'; import DataRequestRepository from '../../datarequest/datarequest.repository'; import QuestionBankService from '../questionbank.service'; import GlobalService from '../../global/global.service'; -import { globalStub, publisherStub, questionStatusMissingEntriesStub, dataRequestSchemaDraftStub } from '../__mocks__/questionbank'; +import * as questionBank from '../__mocks__/questionbank'; import PublisherService from '../../publisher/publisher.service'; import QuestionBankRepository from '../questionbank.repository'; import PublisherRepository from '../../publisher/publisher.repository'; +import * as activeSchemaNotCreatedThroughForm from '../__mocks__/activeSchemaNotCreatedThroughForm'; +import * as activeSchemaCreatedThroughForm from '../__mocks__/activeSchemaCreatedThroughForm'; +import * as draftSchemaNotCreatedThroughForm from '../__mocks__/draftSchemaCreatedThroughForm'; +import * as noSchemaExists from '../__mocks__/noSchemaExists'; describe('Question Bank Service', function () { - it('When no entry exists in the schema', async function () { - //dataSchemaModel - const dataRequestRepository = new DataRequestRepository(); - const dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); - const dataRequestRepositoryStub2 = sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); - - const globalService = new GlobalService(); - const globalServiceStub = sinon.stub(globalService, 'getGlobal').returns(globalStub); + const dataRequestRepository = new DataRequestRepository(); + sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); + const globalService = new GlobalService(); + sinon.stub(globalService, 'getGlobal').returns(questionBank.globalDocument); + const publisherRepository = new PublisherRepository(); + sinon.stub(publisherRepository, 'getPublisher').returns(questionBank.publisherDocument); + const publisherService = new PublisherService(publisherRepository); + const questionBankRepository = new QuestionBankRepository(); + const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + let dataRequestRepositoryStub; + + afterEach(function () { + dataRequestRepositoryStub.restore(); + }); - const publisherRepository = new PublisherRepository(); - const publisherRepositoryStub = sinon.stub(publisherRepository, 'getPublisher').returns({}); + it('No data request schema exists', async function () { + dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); - const publisherService = new PublisherService(publisherRepository); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - const questionBankRepository = new QuestionBankRepository(); + expect(result.questionStatus).toEqual(noSchemaExists.expectedQuestionStatus); + expect(result.guidance).toEqual(noSchemaExists.expectedGuidance); + expect(result.countOfChanges).toEqual(noSchemaExists.expectedCountOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + }); - const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + it('Draft data request schema exists created through the customize form', async function () { + dataRequestRepositoryStub = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([draftSchemaNotCreatedThroughForm.dataRequestSchema]); - const result = await questionBankService.getQuestionBankInfo(publisherStub._id); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - //Il risultato - expect(result.questionStatus).toEqual(questionStatusMissingEntriesStub); - expect(result.guidance).toEqual({}); - expect(result.countOfChanges).toEqual(0); - expect(result.masterSchema).toEqual(globalStub.masterSchema); + expect(result.questionStatus).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.questionStatus); + expect(result.guidance).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.guidance); + expect(result.countOfChanges).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.countOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); - it('When entry exists in draft status', async function () { - //dataSchemaModel - const dataRequestRepository = new DataRequestRepository(); - const dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([dataRequestSchemaDraftStub]); - const dataRequestRepositoryStub2 = sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); + it('Active data request schema exists created through the customize form', async function () { + dataRequestRepositoryStub = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([activeSchemaCreatedThroughForm.dataRequestSchema]); - const globalService = new GlobalService(); - const globalServiceStub = sinon.stub(globalService, 'getGlobal').returns(globalStub); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - const publisherRepository = new PublisherRepository(); - const publisherRepositoryStub = sinon.stub(publisherRepository, 'getPublisher').returns({}); - - const publisherService = new PublisherService(publisherRepository); - - const questionBankRepository = new QuestionBankRepository(); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + expect(result.guidance).toEqual(activeSchemaCreatedThroughForm.expectedGuidance); + expect(result.questionStatus).toEqual(activeSchemaCreatedThroughForm.expectedQuestionStatus); + expect(result.countOfChanges).toEqual(activeSchemaCreatedThroughForm.expectedCountOfChanges); + }); - const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + it('Active data request schema exists not created through the customize form', async function () { + dataRequestRepositoryStub = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([activeSchemaNotCreatedThroughForm.dataRequestSchema]); - const result = await questionBankService.getQuestionBankInfo(publisherStub._id); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - expect(result.questionStatus).toEqual(dataRequestSchemaDraftStub.questionStatus); - expect(result.guidance).toEqual(dataRequestSchemaDraftStub.guidance); - expect(result.countOfChanges).toEqual(dataRequestSchemaDraftStub.countOfChanges); - expect(result.masterSchema).toEqual(globalStub.masterSchema); + expect(result.questionStatus).toEqual(activeSchemaNotCreatedThroughForm.expectedQuestionStatus); + expect(result.guidance).toEqual(activeSchemaNotCreatedThroughForm.expectedGuidance); + expect(result.countOfChanges).toEqual(activeSchemaNotCreatedThroughForm.expectedCountOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); }); diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 7f031669..b47f25a8 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,4 +1,4 @@ -import { isEmpty } from 'lodash'; +import { isEmpty, has } from 'lodash'; export default class QuestionbankService { constructor(questionbankRepository, publisherService, globalService, dataRequestRepository) { @@ -88,7 +88,7 @@ export default class QuestionbankService { //check for new questions in the master schema and add those masterSchema.questionSets.forEach(questionSet => { questionSet.questions.forEach(question => { - if (!Object.keys(latestSchemaVersion.questionStatus).includes(question.questionId)) { + if (!has(latestSchemaVersion.questionStatus, question.questionId)) { newQuestionStatus[question.questionId] = question.defaultQuestion; } }); @@ -118,16 +118,15 @@ export default class QuestionbankService { const guidance = {}; const jsonSchema = latestSchemaVersion.jsonSchema; - let questionIds = []; jsonSchema.questionSets.forEach(questionSet => { - questionIds = [...questionIds, ...questionSet.questions.map(question => question.questionId)]; + questionSet.questions.forEach(question => { + questionStatus[question.questionId] = 1; + }); }); masterSchema.questionSets.forEach(questionSet => { questionSet.questions.forEach(question => { - if (questionIds.includes(question.questionId)) { - questionStatus[question.questionId] = 1; - } else { + if (!has(questionStatus, question.questionId)) { questionStatus[question.questionId] = question.defaultQuestion; } }); From 1ac45b1428623bfbf57eb55b76dc211916e35292 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Thu, 25 Nov 2021 12:18:01 +0000 Subject: [PATCH 007/389] Create functions to generate question status object --- .../questionbank/questionbank.service.js | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index b47f25a8..79919b88 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -30,6 +30,7 @@ export default class QuestionbankService { if (isEmpty(dataRequestSchemas)) { let questionStatus = {}; + //create the questionStatus from the master schema masterSchema.questionSets.forEach(questionSet => { questionSet.questions.forEach(question => { questionStatus[question.questionId] = question.defaultQuestion; @@ -49,27 +50,18 @@ export default class QuestionbankService { return { masterSchema, - questionStatus, - guidance: {}, - countOfChanges: 0, + questionStatus: newSchema.questionStatus, + guidance: newSchema.guidance, + countOfChanges: newSchema.countOfChanges, }; } const latestSchemaVersion = dataRequestSchemas[0]; if (latestSchemaVersion.status === 'draft') { - //check if new questions added in the masterSchema let newQuestionStatus = latestSchemaVersion.questionStatus; - let newQuestionsAdded = false; - - masterSchema.questionSets.forEach(questionSet => { - questionSet.questions.forEach(question => { - if (!Object.keys(latestSchemaVersion.questionStatus).includes(question.questionId)) { - newQuestionStatus[question.questionId] = question.defaultQuestion; - newQuestionsAdded = true; - } - }); - }); + let newQuestionsAdded = this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, newQuestionStatus); + //Add new questions from the master schema if any if (newQuestionsAdded) await this.dataRequestRepository.updateApplicationFormSchemaById(latestSchemaVersion._id, { questionStatus: newQuestionStatus }); @@ -85,14 +77,8 @@ export default class QuestionbankService { if (!isEmpty(latestSchemaVersion.questionStatus)) { let newQuestionStatus = latestSchemaVersion.questionStatus; - //check for new questions in the master schema and add those - masterSchema.questionSets.forEach(questionSet => { - questionSet.questions.forEach(question => { - if (!has(latestSchemaVersion.questionStatus, question.questionId)) { - newQuestionStatus[question.questionId] = question.defaultQuestion; - } - }); - }); + //Add new questions from the master schema if any + this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, newQuestionStatus); const newSchema = { publisher: publisher.name, @@ -113,31 +99,21 @@ export default class QuestionbankService { countOfChanges: newSchema.countOfChanges, }; } else { - //need to create the question status from the current jsonSchema let questionStatus = {}; - const guidance = {}; - const jsonSchema = latestSchemaVersion.jsonSchema; - jsonSchema.questionSets.forEach(questionSet => { - questionSet.questions.forEach(question => { - questionStatus[question.questionId] = 1; - }); - }); + //Add questions from the publisher schema + this.addQuestionsFromPublisherSchema(latestSchemaVersion, questionStatus); - masterSchema.questionSets.forEach(questionSet => { - questionSet.questions.forEach(question => { - if (!has(questionStatus, question.questionId)) { - questionStatus[question.questionId] = question.defaultQuestion; - } - }); - }); + //Add question from master schema if not in the publisher schema + this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, questionStatus); const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus, - guidance, + guidance: {}, + countOfChanges: 0, version: latestSchemaVersion.version + 1, }; @@ -145,12 +121,36 @@ export default class QuestionbankService { return { masterSchema, - questionStatus, - guidance: {}, - countOfChanges: 0, - version: latestSchemaVersion.version + 1, + questionStatus: newSchema.questionStatus, + guidance: newSchema.guidance, + countOfChanges: newSchema.countOfChanges, }; } } } + + addQuestionsFromPublisherSchema(publisherSchema, questionStatus) { + const jsonSchema = publisherSchema.jsonSchema; + jsonSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + questionStatus[question.questionId] = 1; + }); + }); + } + + addQuestionsFromMasterSchema(masterSchema, publisherSchema, questionStatus) { + let newQuestionsAdded = false; + + //Add new questions from the master schema if any + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + if (!has(publisherSchema.questionStatus, question.questionId)) { + questionStatus[question.questionId] = question.defaultQuestion; + newQuestionsAdded = true; + } + }); + }); + + return newQuestionsAdded; + } } From 7365e5975255e90ed1b80b10256ecb036aa09ba7 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Fri, 26 Nov 2021 12:02:59 +0000 Subject: [PATCH 008/389] Add schemaId to the data returned by the get API --- .../datarequest/datarequest.repository.js | 2 +- .../activeSchemaCreatedThroughForm.js | 56 +++++++++---------- .../activeSchemaNotCreatedThroughForm.js | 55 +++++++++--------- .../draftSchemaCreatedThroughForm.js | 2 +- .../questionbank/__mocks__/noSchemaExists.js | 36 ++++++------ .../__tests__/questionbank.service.test.js | 44 +++++++++------ .../questionbank/questionbank.service.js | 16 ++++-- 7 files changed, 115 insertions(+), 96 deletions(-) diff --git a/src/resources/datarequest/datarequest.repository.js b/src/resources/datarequest/datarequest.repository.js index 0d870c91..295a2cfa 100644 --- a/src/resources/datarequest/datarequest.repository.js +++ b/src/resources/datarequest/datarequest.repository.js @@ -171,7 +171,7 @@ export default class DataRequestRepository extends Repository { createApplicationFormSchema(newSchema) { const newSchemaModel = new DataRequestSchemaModel(newSchema); - DataRequestSchemaModel.create(newSchemaModel); + return DataRequestSchemaModel.create(newSchemaModel); } updateApplicationFormSchemaById(id, data, options = {}) { diff --git a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js index f5e53dda..ca1cc68c 100644 --- a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js @@ -25,32 +25,32 @@ export const dataRequestSchema = { }, }; -export const expectedQuestionStatus = { - safedatadataflowdiagramenclosed: 1, - safeoutputsoutputsdisseminationplansproposalfindings: 0, - safeoutputsoutputsdisseminationplansmilestones: 0, - safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, - 'safeoutputs-dataretention-retaindatadate': 0, - safeoutputsdataretentionretaindatadatereason: 1, - safeoutputsdataretentionretaindataextensionpermissions: 0, - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, - safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, -}; - -export const expectedGuidance = { - safedatadataflowdiagramenclosed: 'This is the guidance for safedatadataflowdiagramenclosed', - 'safeoutputs-dataretention-retaindatadate': 'This is the guidance for safeoutputs-dataretention-retaindatadate', +export const expectedSchema = { + questionStatus: { + safedatadataflowdiagramenclosed: 1, + safeoutputsoutputsdisseminationplansproposalfindings: 0, + safeoutputsoutputsdisseminationplansmilestones: 0, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 0, + 'safeoutputs-dataretention-retaindatadate': 0, + safeoutputsdataretentionretaindatadatereason: 1, + safeoutputsdataretentionretaindataextensionpermissions: 0, + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, + }, + guidance: { + safedatadataflowdiagramenclosed: 'This is the guidance for safedatadataflowdiagramenclosed', + 'safeoutputs-dataretention-retaindatadate': 'This is the guidance for safeoutputs-dataretention-retaindatadate', + }, + countOfChanges: 0, }; - -export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js index ceac4331..244a634b 100644 --- a/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js @@ -301,31 +301,32 @@ export const dataRequestSchema = { }; //Combination of the questions in the active schema and the questions in the master schema -export const expectedQuestionStatus = { - safedatadataflowdatatransferedoutsideuk: 1, - safedatadataflowregionsdataprocessed: 1, - safedatadataflowdetailedinformation: 1, - safedatadataflowdiagramenclosed: 1, - safeoutputsoutputsdisseminationplansproposalfindings: 1, - safeoutputsoutputsdisseminationplansmilestones: 1, - safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 1, - 'safeoutputs-dataretention-retaindatadate': 1, - safeoutputsdataretentionretaindatadatereason: 1, - safeoutputsdataretentionretaindataextensionpermissions: 1, - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, - safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, +export const expectedSchema = { + questionStatus: { + safedatadataflowdatatransferedoutsideuk: 1, + safedatadataflowregionsdataprocessed: 1, + safedatadataflowdetailedinformation: 1, + safedatadataflowdiagramenclosed: 1, + safeoutputsoutputsdisseminationplansproposalfindings: 1, + safeoutputsoutputsdisseminationplansmilestones: 1, + safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy: 1, + 'safeoutputs-dataretention-retaindatadate': 1, + safeoutputsdataretentionretaindatadatereason: 1, + safeoutputsdataretentionretaindataextensionpermissions: 1, + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, + }, + guidance: {}, + countOfChanges: 0, }; - -export const expectedGuidance = {}; -export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js index d45f25d5..00779cec 100644 --- a/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js @@ -1,5 +1,5 @@ export const dataRequestSchema = { - _id: { $oid: '619baf36eb175aaacd592bbb' }, + _id: '619baf36eb175aaacd592bbb', version: 1, dataSetId: '', publisher: 'ALLIANCE > SAIL', diff --git a/src/resources/questionbank/__mocks__/noSchemaExists.js b/src/resources/questionbank/__mocks__/noSchemaExists.js index 7140153a..04493ff0 100644 --- a/src/resources/questionbank/__mocks__/noSchemaExists.js +++ b/src/resources/questionbank/__mocks__/noSchemaExists.js @@ -1,18 +1,20 @@ -export const expectedQuestionStatus = { - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, - safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, +export const expectedSchema = { + _id: '619baf36eb175aaacd592bbb', + questionStatus: { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantorcid: 1, + safepeopleprimaryapplicantemail: 0, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + safepeopleotherindividualsrole: 0, + safepeopleotherindividualsaccessdata: 1, + safepeopleotherindividualsaccreditedresearcher: 0, + safepeopleotherindividualstraininginformationgovernance: 0, + safepeopleotherindividualsexperience: 1, + }, + guidance: {}, + countOfChanges: 0, }; - -export const expectedGuidance = {}; -export const expectedCountOfChanges = 0; diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index c4935de3..462d6982 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -13,7 +13,6 @@ import * as noSchemaExists from '../__mocks__/noSchemaExists'; describe('Question Bank Service', function () { const dataRequestRepository = new DataRequestRepository(); - sinon.stub(dataRequestRepository, 'createApplicationFormSchema'); const globalService = new GlobalService(); sinon.stub(globalService, 'getGlobal').returns(questionBank.globalDocument); const publisherRepository = new PublisherRepository(); @@ -21,25 +20,30 @@ describe('Question Bank Service', function () { const publisherService = new PublisherService(publisherRepository); const questionBankRepository = new QuestionBankRepository(); const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); - let dataRequestRepositoryStub; + let dataRequestRepositoryStubGet; + let dataRequestRepositoryStubCreate; afterEach(function () { - dataRequestRepositoryStub.restore(); + dataRequestRepositoryStubGet.restore(); + dataRequestRepositoryStubCreate.restore(); }); it('No data request schema exists', async function () { - dataRequestRepositoryStub = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); + dataRequestRepositoryStubGet = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(noSchemaExists.expectedSchema); const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - expect(result.questionStatus).toEqual(noSchemaExists.expectedQuestionStatus); - expect(result.guidance).toEqual(noSchemaExists.expectedGuidance); - expect(result.countOfChanges).toEqual(noSchemaExists.expectedCountOfChanges); + expect(result.questionStatus).toEqual(noSchemaExists.expectedSchema.questionStatus); + expect(result.guidance).toEqual(noSchemaExists.expectedSchema.guidance); + expect(result.countOfChanges).toEqual(noSchemaExists.expectedSchema.countOfChanges); expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); it('Draft data request schema exists created through the customize form', async function () { - dataRequestRepositoryStub = sinon + dataRequestRepositoryStubGet = sinon .stub(dataRequestRepository, 'getApplicationFormSchemas') .returns([draftSchemaNotCreatedThroughForm.dataRequestSchema]); @@ -52,28 +56,36 @@ describe('Question Bank Service', function () { }); it('Active data request schema exists created through the customize form', async function () { - dataRequestRepositoryStub = sinon + dataRequestRepositoryStubGet = sinon .stub(dataRequestRepository, 'getApplicationFormSchemas') .returns([activeSchemaCreatedThroughForm.dataRequestSchema]); + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(activeSchemaCreatedThroughForm.expectedSchema); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); - expect(result.guidance).toEqual(activeSchemaCreatedThroughForm.expectedGuidance); - expect(result.questionStatus).toEqual(activeSchemaCreatedThroughForm.expectedQuestionStatus); - expect(result.countOfChanges).toEqual(activeSchemaCreatedThroughForm.expectedCountOfChanges); + expect(result.guidance).toEqual(activeSchemaCreatedThroughForm.expectedSchema.guidance); + expect(result.questionStatus).toEqual(activeSchemaCreatedThroughForm.expectedSchema.questionStatus); + expect(result.countOfChanges).toEqual(activeSchemaCreatedThroughForm.expectedSchema.countOfChanges); }); it('Active data request schema exists not created through the customize form', async function () { - dataRequestRepositoryStub = sinon + dataRequestRepositoryStubGet = sinon .stub(dataRequestRepository, 'getApplicationFormSchemas') .returns([activeSchemaNotCreatedThroughForm.dataRequestSchema]); + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(activeSchemaNotCreatedThroughForm.expectedSchema); + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - expect(result.questionStatus).toEqual(activeSchemaNotCreatedThroughForm.expectedQuestionStatus); - expect(result.guidance).toEqual(activeSchemaNotCreatedThroughForm.expectedGuidance); - expect(result.countOfChanges).toEqual(activeSchemaNotCreatedThroughForm.expectedCountOfChanges); + expect(result.questionStatus).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.questionStatus); + expect(result.guidance).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.guidance); + expect(result.countOfChanges).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.countOfChanges); expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); }); diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 79919b88..e387700a 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -46,13 +46,14 @@ export default class QuestionbankService { countOfChanges: 0, }; - await this.dataRequestRepository.createApplicationFormSchema(newSchema); + const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, - questionStatus: newSchema.questionStatus, - guidance: newSchema.guidance, - countOfChanges: newSchema.countOfChanges, + questionStatus: schema.questionStatus, + guidance: schema.guidance, + countOfChanges: schema.countOfChanges, + schemaId: schema._id, }; } @@ -70,6 +71,7 @@ export default class QuestionbankService { questionStatus: newQuestionStatus, guidance: latestSchemaVersion.guidance, countOfChanges: latestSchemaVersion.countOfChanges, + schemaId: latestSchemaVersion._id, }; } @@ -90,13 +92,14 @@ export default class QuestionbankService { countOfChanges: 0, }; - await this.dataRequestRepository.createApplicationFormSchema(newSchema); + const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, questionStatus: newSchema.questionStatus, guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, + schemaId: schema._id, }; } else { let questionStatus = {}; @@ -117,13 +120,14 @@ export default class QuestionbankService { version: latestSchemaVersion.version + 1, }; - await this.dataRequestRepository.createApplicationFormSchema(newSchema); + const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); return { masterSchema, questionStatus: newSchema.questionStatus, guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, + schemaId: schema._id, }; } } From 582f4dc892cdd8e48e75f051de621e5a4eac0ef9 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Fri, 26 Nov 2021 12:09:54 +0000 Subject: [PATCH 009/389] Move masterSchema into test folder --- src/resources/questionbank/{ => __mocks__}/masterSchema.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/resources/questionbank/{ => __mocks__}/masterSchema.json (100%) diff --git a/src/resources/questionbank/masterSchema.json b/src/resources/questionbank/__mocks__/masterSchema.json similarity index 100% rename from src/resources/questionbank/masterSchema.json rename to src/resources/questionbank/__mocks__/masterSchema.json From 6b66f46a2d7b82a5c9edf46737915a1241fb6c27 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Fri, 26 Nov 2021 12:22:26 +0000 Subject: [PATCH 010/389] Remove unnecessary classes --- .../questionbank/__mocks__/masterSchema.json | 1 + .../__tests__/questionbank.service.test.js | 4 +--- src/resources/questionbank/dependency.js | 4 +--- .../questionbank/questionbank.entity.js | 8 -------- .../questionbank/questionbank.model.js | 19 ------------------- .../questionbank/questionbank.repository.js | 19 ------------------- .../questionbank/questionbank.service.js | 15 +-------------- 7 files changed, 4 insertions(+), 66 deletions(-) delete mode 100644 src/resources/questionbank/questionbank.entity.js delete mode 100644 src/resources/questionbank/questionbank.model.js delete mode 100644 src/resources/questionbank/questionbank.repository.js diff --git a/src/resources/questionbank/__mocks__/masterSchema.json b/src/resources/questionbank/__mocks__/masterSchema.json index 0e09f4f2..25dc5c34 100644 --- a/src/resources/questionbank/__mocks__/masterSchema.json +++ b/src/resources/questionbank/__mocks__/masterSchema.json @@ -2330,3 +2330,4 @@ ] } } +git pus \ No newline at end of file diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index 462d6982..d3e7c0aa 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -4,7 +4,6 @@ import QuestionBankService from '../questionbank.service'; import GlobalService from '../../global/global.service'; import * as questionBank from '../__mocks__/questionbank'; import PublisherService from '../../publisher/publisher.service'; -import QuestionBankRepository from '../questionbank.repository'; import PublisherRepository from '../../publisher/publisher.repository'; import * as activeSchemaNotCreatedThroughForm from '../__mocks__/activeSchemaNotCreatedThroughForm'; import * as activeSchemaCreatedThroughForm from '../__mocks__/activeSchemaCreatedThroughForm'; @@ -18,8 +17,7 @@ describe('Question Bank Service', function () { const publisherRepository = new PublisherRepository(); sinon.stub(publisherRepository, 'getPublisher').returns(questionBank.publisherDocument); const publisherService = new PublisherService(publisherRepository); - const questionBankRepository = new QuestionBankRepository(); - const questionBankService = new QuestionBankService(questionBankRepository, publisherService, globalService, dataRequestRepository); + const questionBankService = new QuestionBankService(publisherService, globalService, dataRequestRepository); let dataRequestRepositoryStubGet; let dataRequestRepositoryStubCreate; diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js index 67d88ac3..963889ac 100644 --- a/src/resources/questionbank/dependency.js +++ b/src/resources/questionbank/dependency.js @@ -1,9 +1,7 @@ -import QuestionbankRepository from './questionbank.repository'; import QuestionbankService from './questionbank.service'; import { publisherService } from '../publisher/dependency'; import { globalService } from '../global/dependency'; import DataRequestRepository from '../datarequest/datarequest.repository'; -export const questionbankRepository = new QuestionbankRepository(); export const dataRequestRepository = new DataRequestRepository(); -export const questionbankService = new QuestionbankService(questionbankRepository, publisherService, globalService, dataRequestRepository); +export const questionbankService = new QuestionbankService(publisherService, globalService, dataRequestRepository); diff --git a/src/resources/questionbank/questionbank.entity.js b/src/resources/questionbank/questionbank.entity.js deleted file mode 100644 index 9836a64b..00000000 --- a/src/resources/questionbank/questionbank.entity.js +++ /dev/null @@ -1,8 +0,0 @@ -import Entity from '../base/entity'; - -export default class QuestionbankClass extends Entity { - constructor(obj) { - super(); - Object.assign(this, obj); - } -} diff --git a/src/resources/questionbank/questionbank.model.js b/src/resources/questionbank/questionbank.model.js deleted file mode 100644 index 244e91d5..00000000 --- a/src/resources/questionbank/questionbank.model.js +++ /dev/null @@ -1,19 +0,0 @@ -import { model, Schema } from 'mongoose'; - -import QuestionbankClass from './questionbank.entity'; - -const questionbankSchema = new Schema( - { - // New object properties here - }, - { - timestamps: true, - toJSON: { virtuals: true }, - toObject: { virtuals: true }, - } -); - -// Load entity class -questionbankSchema.loadClass(QuestionbankClass); - -export const Questionbank = model('Questionbank', questionbankSchema); \ No newline at end of file diff --git a/src/resources/questionbank/questionbank.repository.js b/src/resources/questionbank/questionbank.repository.js deleted file mode 100644 index 4558938f..00000000 --- a/src/resources/questionbank/questionbank.repository.js +++ /dev/null @@ -1,19 +0,0 @@ -import Repository from '../base/repository'; -import { Questionbank } from './questionbank.model'; -import constants from '../utilities/constants.util'; - -export default class QuestionbankRepository extends Repository { - constructor() { - super(Questionbank); - this.questionbank = Questionbank; - } - - async getQuestionbank(query, options) { - return this.findOne(query, options); - } - - async getQuestionbanks(query) { - const options = { lean: true }; - return this.find(query, options); - } -} diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index e387700a..b5901b92 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,25 +1,12 @@ import { isEmpty, has } from 'lodash'; export default class QuestionbankService { - constructor(questionbankRepository, publisherService, globalService, dataRequestRepository) { - this.questionbankRepository = questionbankRepository; + constructor(publisherService, globalService, dataRequestRepository) { this.publisherService = publisherService; this.globalService = globalService; this.dataRequestRepository = dataRequestRepository; } - getQuestionbank(id, query = {}, options = {}) { - // Protect for no id passed - if (!id) return; - - query = { ...query, id }; - return this.questionbankRepository.getQuestionbank(query, options); - } - - getQuestionbanks(query = {}) { - return this.questionbankRepository.getQuestionbanks(query); - } - async getQuestionBankInfo(publisherId) { const publisher = await this.publisherService.getPublisher(publisherId); const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); From a065c78ee0ec6bf289d70649f647550ad8b8a6c6 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Mon, 29 Nov 2021 13:40:07 +0000 Subject: [PATCH 011/389] implement PATCH api for data request schema --- .../schema/datarequest.schema.controller.js | 91 +++++++++++++++++++ .../schema/datarequest.schema.repository.js | 18 ++++ .../schema/datarequest.schema.service.js | 25 +++++ .../schema/datarequest.schemas.route.js | 47 ++++++++++ .../datarequest/schema/dependency.js | 5 + 5 files changed, 186 insertions(+) create mode 100644 src/resources/datarequest/schema/datarequest.schema.controller.js create mode 100644 src/resources/datarequest/schema/datarequest.schema.repository.js create mode 100644 src/resources/datarequest/schema/datarequest.schema.service.js create mode 100644 src/resources/datarequest/schema/dependency.js diff --git a/src/resources/datarequest/schema/datarequest.schema.controller.js b/src/resources/datarequest/schema/datarequest.schema.controller.js new file mode 100644 index 00000000..dd7add23 --- /dev/null +++ b/src/resources/datarequest/schema/datarequest.schema.controller.js @@ -0,0 +1,91 @@ +import Controller from '../../base/controller'; +import { logger } from '../../utilities/logger'; + +const logCategory = 'datarequestschema'; + +export default class DatarequestschemaController extends Controller { + constructor(datarequestschemaService) { + super(datarequestschemaService); + this.datarequestschemaService = datarequestschemaService; + } + + async getDatarequestschema(req, res) { + try { + // Extract id parameter from query string + const { id } = req.params; + // If no id provided, it is a bad request + if (!id) { + return res.status(400).json({ + success: false, + message: 'You must provide a datarequestschema identifier', + }); + } + // Find the datarequestschema + const datarequestschema = await this.datarequestschemaService.getDatarequestschemaById(id); + // Return if no datarequestschema found + if (!datarequestschema) { + return res.status(404).json({ + success: false, + message: 'A datarequestschema could not be found with the provided id', + }); + } + // Return the datarequestschema + return res.status(200).json({ + success: true, + ...datarequestschema, + }); + } 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', + }); + } + } + + async getDatarequestschemas(req, res) { + try { + // Find the relevant datarequestschemas + const datarequestschemas = await this.datarequestschemaService.getDatarequestschemas(req.query).catch(err => { + logger.logError(err, logCategory); + }); + // Return the datarequestschemas + return res.status(200).json({ + success: true, + data: datarequestschemas, + }); + } 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', + }); + } + } + + async updateDatarequestschema(req, res) { + try { + const id = req.params.id; + const updatedSchema = req.body; + + // Find the relevant datarequestschemas + const datarequestschema = await this.datarequestschemaService.updateDatarequestschema(id, updatedSchema).catch(err => { + logger.logError(err, logCategory); + }); + // Return the datarequestschemas + return res.status(200).json({ + success: true, + data: datarequestschema, + }); + } 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', + }); + } + } +} diff --git a/src/resources/datarequest/schema/datarequest.schema.repository.js b/src/resources/datarequest/schema/datarequest.schema.repository.js new file mode 100644 index 00000000..3dec30c6 --- /dev/null +++ b/src/resources/datarequest/schema/datarequest.schema.repository.js @@ -0,0 +1,18 @@ +import Repository from '../../base/repository'; +import { DataRequestSchemaModel } from './datarequest.schemas.model'; + +export default class DatarequestschemaRepository extends Repository { + constructor() { + super(DataRequestSchemaModel); + this.datarequestschema = DataRequestSchemaModel; + } + + async getDatarequestschema(query, options) { + return this.findOne(query, options); + } + + async getDatarequestschemas(query) { + const options = { lean: true }; + return this.find(query, options); + } +} diff --git a/src/resources/datarequest/schema/datarequest.schema.service.js b/src/resources/datarequest/schema/datarequest.schema.service.js new file mode 100644 index 00000000..529041a4 --- /dev/null +++ b/src/resources/datarequest/schema/datarequest.schema.service.js @@ -0,0 +1,25 @@ +export default class DatarequestschemaService { + constructor(datarequestschemaRepository) { + this.datarequestschemaRepository = datarequestschemaRepository; + } + + getDatarequestschema(id, query = {}, options = {}) { + // Protect for no id passed + if (!id) return; + + query = { ...query, id }; + return this.datarequestschemaRepository.getDatarequestschema(query, options); + } + + getDatarequestschemaById(id) { + return this.datarequestschemaRepository.findById(id, { lean: true }); + } + + getDatarequestschemas(query = {}) { + return this.datarequestschemaRepository.getDatarequestschemas(query); + } + + updateDatarequestschema(id, datarequestschema) { + return this.datarequestschemaRepository.update(id, datarequestschema); + } +} diff --git a/src/resources/datarequest/schema/datarequest.schemas.route.js b/src/resources/datarequest/schema/datarequest.schemas.route.js index f6231bdd..3ce198c9 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.route.js +++ b/src/resources/datarequest/schema/datarequest.schemas.route.js @@ -1,12 +1,55 @@ import express from 'express'; import passport from 'passport'; +import { isNull, isUndefined, isEmpty } from 'lodash'; import { DataRequestSchemaModel } from './datarequest.schemas.model'; +import DatarequestschemaController from './datarequest.schema.controller'; +import { datarequestschemaService } from './dependency'; import { utils } from '../../auth'; import { ROLES } from '../../user/user.roles'; +const datarequestschemaController = new DatarequestschemaController(datarequestschemaService); + const router = express.Router(); +function isUserMemberOfTeam(user, publisherName) { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher.name === publisherName); +} + +const validateUpdate = (req, res, next) => { + const { id } = req.params; + + if (isUndefined(id)) return res.status(400).json({ success: false, message: 'You must provide a valid data request Id' }); + + next(); +}; + +const authorizeUpdate = async (req, res, next) => { + const requestingUser = req.user; + const { id } = req.params; + + const datarequestschema = await datarequestschemaService.getDatarequestschemaById(id); + + if (isEmpty(datarequestschema)) { + return res.status(404).json({ + success: false, + message: 'The requested data request schema could not be found', + }); + } + + const authorised = isUserMemberOfTeam(requestingUser, datarequestschema.publisher); + + if (!authorised) { + return res.status(401).json({ + success: false, + message: 'You are not authorised to perform this action', + }); + } + + next(); +}; + // @router POST api/v1/data-access-request/schema // @desc Add a data request schema // @access Private @@ -37,6 +80,10 @@ router.get('/', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin, R return res.json({ jsonSchema: dataRequestSchema.jsonSchema }); }); +router.patch('/:id', passport.authenticate('jwt'), validateUpdate, authorizeUpdate, (req, res) => + datarequestschemaController.updateDatarequestschema(req, res) +); + module.exports = router; async function archiveOtherVersions(id, dataSetId, status) { diff --git a/src/resources/datarequest/schema/dependency.js b/src/resources/datarequest/schema/dependency.js new file mode 100644 index 00000000..06802a55 --- /dev/null +++ b/src/resources/datarequest/schema/dependency.js @@ -0,0 +1,5 @@ +import DatarequestschemaRepository from './datarequest.schema.repository'; +import DatarequestschemaService from './datarequest.schema.service'; + +export const datarequestschemaRepository = new DatarequestschemaRepository(); +export const datarequestschemaService = new DatarequestschemaService(datarequestschemaRepository); From 7220138c4373a6a2158a3c323663b68df3c4e47a Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 3 Dec 2021 10:15:02 +0000 Subject: [PATCH 012/389] Moved userIsManager check into middleware function --- src/resources/auth/utils.js | 13 +++++++++++++ .../publisher/publisher.controller.js | 18 ------------------ src/resources/publisher/publisher.route.js | 3 ++- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 97cb301b..37a7ad5b 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -127,6 +127,18 @@ const getTeams = async () => { return teams; }; +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)) return next(); + + return res.status(401).json({ + status: 'error', + message: 'Unauthorised to perform this action.', + }); +}; + const catchLoginErrorAndRedirect = (req, res, next) => { if (req.auth.err || !req.auth.user) { if (req.auth.err === 'loginError') { @@ -213,4 +225,5 @@ export { getTeams, catchLoginErrorAndRedirect, loginAndSignToken, + userIsTeamManager, }; diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index b41cf6d2..c4f49d24 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -165,24 +165,6 @@ export default class PublisherController extends Controller { async updateDataRequestModalContent(req, res) { try { - const { _id: requestingUserId } = req.user; - - const team = await TeamModel.findOne({ _id: req.params.id }) - .populate({ - path: 'users', - populate: { - path: 'additionalInfo', - select: 'organisation bio showOrganisation showBio', - }, - }) - .lean(); - - // Check that user is a manager - const authorised = teamController.checkTeamPermissions(constants.roleTypes.MANAGER, team, requestingUserId); - if (!authorised) { - return res.status(401).json({ success: false }); - } - await this.publisherService.updateDataRequestModalContent(req.params.id, req.user.id, req.body.content).then(() => { return res.status(200).json({ success: true }); }); diff --git a/src/resources/publisher/publisher.route.js b/src/resources/publisher/publisher.route.js index 6817ef48..43cbcb99 100644 --- a/src/resources/publisher/publisher.route.js +++ b/src/resources/publisher/publisher.route.js @@ -4,6 +4,7 @@ import passport from 'passport'; import { logger } from '../utilities/logger'; import PublisherController from './publisher.controller'; import { publisherService, workflowService, dataRequestService, amendmentService } from './dependency'; +import { utils } from '../auth'; const logCategory = 'Publisher'; const publisherController = new PublisherController(publisherService, workflowService, dataRequestService, amendmentService); @@ -54,7 +55,7 @@ router.get( (req, res) => publisherController.getPublisherWorkflows(req, res) ); -router.patch('/dataRequestModalContent/:id', passport.authenticate('jwt'), (req, res) => +router.patch('/dataRequestModalContent/:id', passport.authenticate('jwt'), utils.userIsTeamManager(), (req, res) => publisherController.updateDataRequestModalContent(req, res) ); From c54a95173b909ac6aa2985a5ceff9f1dee4031f6 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 3 Dec 2021 15:03:47 +0000 Subject: [PATCH 013/389] Removing unused import --- src/resources/publisher/publisher.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index c4f49d24..b124b197 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -1,10 +1,8 @@ import _ from 'lodash'; - import constants from '../utilities/constants.util'; import teamController from '../team/team.controller'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; -import { TeamModel } from '../team/team.model'; const logCategory = 'Publisher'; From 1d70eba2059e958314bebd0fd0cc0b17ed2d74c0 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Wed, 8 Dec 2021 11:43:23 +0000 Subject: [PATCH 014/389] add post API for question bank --- .../__tests__/questionbank.service.test.js | 16 ++++++ .../questionbank/questionbank.controller.js | 16 ++++++ .../questionbank/questionbank.route.js | 49 +++++++++++++++++-- .../questionbank/questionbank.service.js | 24 +++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index d3e7c0aa..5ce9d613 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -87,3 +87,19 @@ describe('Question Bank Service', function () { expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); }); + +describe('Question Bank Service, schema creation', function () { + // Pull the master schema + // Get the question status and guidance + // Loops through the question sets on master schema + // If question status is off then remove the question + // If question is on check the if guidance is changed and update the guidance + // Remove the extra options that are not needed by the final version of a schema + // Locked question option + // Default question option + // Save final schema as a new version along with question status and guidance objects + // If first time for 5 safes + // Set the flags that are needed for 5 safes to be functional + + it('creation of a new data request schema'); +}); diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index 44b4f152..9ae455b2 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -49,4 +49,20 @@ export default class QuestionbankController extends Controller { }); } } + + async publishSchema(req, res) { + try { + const { questionStatus, guidance, dataRequestSchema } = req.body; + + const newRequestSchema = this.questionbankService.publishSchema({ questionStatus, guidance, dataRequestSchema }); + + 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', + }); + } + } } diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index 55632d64..421bb034 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -1,8 +1,9 @@ import express from 'express'; import passport from 'passport'; -import { isUndefined, isNull } from 'lodash'; +import { isUndefined, isNull, isEmpty } from 'lodash'; import QuestionbankController from './questionbank.controller'; import { questionbankService } from './dependency'; +import { datarequestschemaService } from './../datarequest/schema/dependency'; import { logger } from '../utilities/logger'; const router = express.Router(); @@ -22,7 +23,7 @@ const validateViewRequest = (req, res, next) => { next(); }; -const authorizeView = (req, res, next) => { +const authorizeViewRequest = (req, res, next) => { const requestingUser = req.user; const { publisherId } = req.params; @@ -38,6 +39,41 @@ const authorizeView = (req, res, next) => { 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' }); + + next(); +}; + +const authorizePostRequest = async (req, res, next) => { + const requestingUser = req.user; + const { id } = req.params; + + const datarequestschema = await datarequestschemaService.getDatarequestschemaById(id); + + if (isEmpty(datarequestschema)) { + return res.status(404).json({ + success: false, + message: 'The requested data request schema could not be found', + }); + } + + const authorised = isUserMemberOfTeam(requestingUser, datarequestschema.publisher); + + if (!authorised) { + return res.status(401).json({ + success: false, + message: 'You are not authorised to perform this action', + }); + } + + req.body.datarequestschema = datarequestschema; + + next(); +}; + // @route GET /api/v1/questionbanks/publisherId // @desc Returns questionbank info belonging to the publisher // @access Public @@ -45,7 +81,7 @@ router.get( '/:publisherId', passport.authenticate('jwt'), validateViewRequest, - authorizeView, + authorizeViewRequest, logger.logRequestMiddleware({ logCategory, action: 'Viewed questionbank data' }), (req, res) => questionbankController.getQuestionbank(req, res) ); @@ -57,4 +93,11 @@ router.get('/', logger.logRequestMiddleware({ logCategory, action: 'Viewed quest questionbankController.getQuestionbanks(req, res) ); +// @route POST /api/v1/questionbanks +// @desc Activate a draft schema creating a jsonSchema from masterSchema +// @access Public +router.post('/schemaId', passport.authenticate('jwt'), validatePostRequest, authorizePostRequest, (req, res) => + questionbankController.publishSchema(req, res) +); + module.exports = router; diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index b5901b92..f1595028 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -120,6 +120,30 @@ export default class QuestionbankService { } } + async publishSchema(schema) { + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const masterSchema = global.masterSchema; + const { guidance, questionStatus } = schema; + + masterSchema.questionSets.forEach((questionSet, questionSetIndex) => { + questionSet.questions.forEach((question, questionIndex) => { + if (questionStatus[question.questionId] === 0) { + delete masterSchema.questionSets[questionSetIndex].questions[questionIndex]; + } else { + if (has(guidance, question.questionId)) question.guidance = guidance[question.questionId]; + delete masterSchema.questionSets[questionSetIndex].questions[questionIndex].lockedQuestion; + delete masterSchema.questionSets[questionSetIndex].questions[questionIndex].defaultQuestion; + } + }); + }); + + const jsonSchema = masterSchema; + + const publishedSchema = await this.dataRequestRepository.updateApplicationFormSchemaById(schema._id, { jsonSchema }); + + return publishedSchema; + } + addQuestionsFromPublisherSchema(publisherSchema, questionStatus) { const jsonSchema = publisherSchema.jsonSchema; jsonSchema.questionSets.forEach(questionSet => { From b5cc803566ff4c6fd8d3084385ab926a90ac28eb Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Thu, 9 Dec 2021 10:03:36 +0000 Subject: [PATCH 015/389] Add flags settings on publisher and datasets --- src/resources/dataset/dataset.repository.js | 5 +++++ src/resources/dataset/v1/dataset.service.js | 4 ++++ src/resources/publisher/publisher.service.js | 4 ++++ src/resources/questionbank/dependency.js | 3 ++- .../questionbank/questionbank.service.js | 16 +++++++++++++++- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/resources/dataset/dataset.repository.js b/src/resources/dataset/dataset.repository.js index 44f01a3d..45355bec 100644 --- a/src/resources/dataset/dataset.repository.js +++ b/src/resources/dataset/dataset.repository.js @@ -34,4 +34,9 @@ export default class DatasetRepository extends Repository { return obj; }, {}); } + + async updateMany(query, data) { + this.dataset.updateMany(query, data); + //ToolModel.updateMany({ 'datasetfields.publisher': 'ALLIANCE > NHS DIGITAL' }, { $set: { is5Safes: false } }) + } } diff --git a/src/resources/dataset/v1/dataset.service.js b/src/resources/dataset/v1/dataset.service.js index de60f40b..245f2848 100644 --- a/src/resources/dataset/v1/dataset.service.js +++ b/src/resources/dataset/v1/dataset.service.js @@ -938,6 +938,10 @@ function populateV2datasetObject(v2Data) { return datasetv2Object; } +export async function updateMany(query, data) { + return await Data.updateMany(query, data); +} + function checkForArray(value) { if (typeof value !== 'string') return value; try { diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index c503938b..63c558d0 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -94,4 +94,8 @@ export default class PublisherService { return filteredApplications; } + + async update(document, body = {}) { + return this.publisherRepository.update(document, body); + } } diff --git a/src/resources/questionbank/dependency.js b/src/resources/questionbank/dependency.js index 963889ac..109f3ed9 100644 --- a/src/resources/questionbank/dependency.js +++ b/src/resources/questionbank/dependency.js @@ -1,7 +1,8 @@ import QuestionbankService from './questionbank.service'; import { publisherService } from '../publisher/dependency'; import { globalService } from '../global/dependency'; +import { datasetService } from '../dataset/dependency'; import DataRequestRepository from '../datarequest/datarequest.repository'; export const dataRequestRepository = new DataRequestRepository(); -export const questionbankService = new QuestionbankService(publisherService, globalService, dataRequestRepository); +export const questionbankService = new QuestionbankService(publisherService, globalService, dataRequestRepository, datasetService); diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index f1595028..45a876ee 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,10 +1,11 @@ import { isEmpty, has } from 'lodash'; export default class QuestionbankService { - constructor(publisherService, globalService, dataRequestRepository) { + constructor(publisherService, globalService, dataRequestRepository, datasetService) { this.publisherService = publisherService; this.globalService = globalService; this.dataRequestRepository = dataRequestRepository; + this.datasetService = datasetService; } async getQuestionBankInfo(publisherId) { @@ -141,6 +142,19 @@ export default class QuestionbankService { const publishedSchema = await this.dataRequestRepository.updateApplicationFormSchemaById(schema._id, { jsonSchema }); + //if its not already a 5 safes publisher then set the flags to true on the publisher and also on the datasets + const publisher = await this.publisherService.getPublisher(schema.publisher); + if (!has(publisher, 'uses5Safes')) { + await this.publisherService.update(publisher._id, { + allowsMessaging: true, + workflowEnabled: true, + allowAccessRequestManagement: true, + uses5Safes: true, + }); + + await this.datasetService.updateMany({ 'datasetfields.publisher': schema.publisher }, { is5Safes: false }); + } + return publishedSchema; } From 97de86645cea49eb7e3c051b13fa9a9d4b07b0b8 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Thu, 9 Dec 2021 16:32:04 +0000 Subject: [PATCH 016/389] fix jsonSchema generation --- .../schema/datarequest.schemas.route.js | 3 ++ src/resources/dataset/dataset.repository.js | 3 +- src/resources/dataset/dataset.service.js | 6 +++- src/resources/dataset/v1/dataset.service.js | 4 --- .../questionbank/questionbank.controller.js | 4 +-- .../questionbank/questionbank.route.js | 28 +++++++++---------- .../questionbank/questionbank.service.js | 20 +++++++------ 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/resources/datarequest/schema/datarequest.schemas.route.js b/src/resources/datarequest/schema/datarequest.schemas.route.js index 3ce198c9..93a35542 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.route.js +++ b/src/resources/datarequest/schema/datarequest.schemas.route.js @@ -80,6 +80,9 @@ router.get('/', passport.authenticate('jwt'), utils.checkIsInRole(ROLES.Admin, R return res.json({ jsonSchema: dataRequestSchema.jsonSchema }); }); +// @router PATCH /api/v1/data-access-request/schema +// @desc patch a data request schema +// @access Private router.patch('/:id', passport.authenticate('jwt'), validateUpdate, authorizeUpdate, (req, res) => datarequestschemaController.updateDatarequestschema(req, res) ); diff --git a/src/resources/dataset/dataset.repository.js b/src/resources/dataset/dataset.repository.js index 45355bec..9adf56a5 100644 --- a/src/resources/dataset/dataset.repository.js +++ b/src/resources/dataset/dataset.repository.js @@ -36,7 +36,6 @@ export default class DatasetRepository extends Repository { } async updateMany(query, data) { - this.dataset.updateMany(query, data); - //ToolModel.updateMany({ 'datasetfields.publisher': 'ALLIANCE > NHS DIGITAL' }, { $set: { is5Safes: false } }) + return this.dataset.updateMany(query, data); } } diff --git a/src/resources/dataset/dataset.service.js b/src/resources/dataset/dataset.service.js index 5bd76087..d52938f6 100644 --- a/src/resources/dataset/dataset.service.js +++ b/src/resources/dataset/dataset.service.js @@ -37,7 +37,7 @@ export default class DatasetService { return dataset; } - async getDatasets(query = {}, options = {} ) { + async getDatasets(query = {}, options = {}) { return this.datasetRepository.getDatasets(query, options); } @@ -110,4 +110,8 @@ export default class DatasetService { }); return dataset; } + + async updateMany(query, data) { + return this.datasetRepository.updateMany(query, data); + } } diff --git a/src/resources/dataset/v1/dataset.service.js b/src/resources/dataset/v1/dataset.service.js index 245f2848..de60f40b 100644 --- a/src/resources/dataset/v1/dataset.service.js +++ b/src/resources/dataset/v1/dataset.service.js @@ -938,10 +938,6 @@ function populateV2datasetObject(v2Data) { return datasetv2Object; } -export async function updateMany(query, data) { - return await Data.updateMany(query, data); -} - function checkForArray(value) { if (typeof value !== 'string') return value; try { diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index 9ae455b2..f691d947 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -52,9 +52,9 @@ export default class QuestionbankController extends Controller { async publishSchema(req, res) { try { - const { questionStatus, guidance, dataRequestSchema } = req.body; + const { dataRequestSchema } = req.body; - const newRequestSchema = this.questionbankService.publishSchema({ questionStatus, guidance, dataRequestSchema }); + const newRequestSchema = await this.questionbankService.publishSchema(dataRequestSchema); return res.status(200).json({ success: true, result: newRequestSchema }); } catch (err) { diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index 421bb034..83edd96b 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -10,11 +10,16 @@ const router = express.Router(); const questionbankController = new QuestionbankController(questionbankService); const logCategory = 'questionbank'; -function isUserMemberOfTeam(user, teamId) { +function isUserMemberOfTeamById(user, teamId) { let { teams } = user; return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); } +function isUserMemberOfTeamByName(user, publisherName) { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher.name === publisherName); +} + const validateViewRequest = (req, res, next) => { const { publisherId } = req.params; @@ -27,7 +32,7 @@ const authorizeViewRequest = (req, res, next) => { const requestingUser = req.user; const { publisherId } = req.params; - const authorised = isUserMemberOfTeam(requestingUser, publisherId); + const authorised = isUserMemberOfTeamById(requestingUser, publisherId); if (!authorised) { return res.status(401).json({ @@ -49,18 +54,18 @@ const validatePostRequest = (req, res, next) => { const authorizePostRequest = async (req, res, next) => { const requestingUser = req.user; - const { id } = req.params; + const { schemaId } = req.params; - const datarequestschema = await datarequestschemaService.getDatarequestschemaById(id); + const dataRequestSchema = await datarequestschemaService.getDatarequestschemaById(schemaId); - if (isEmpty(datarequestschema)) { + if (isEmpty(dataRequestSchema)) { return res.status(404).json({ success: false, message: 'The requested data request schema could not be found', }); } - const authorised = isUserMemberOfTeam(requestingUser, datarequestschema.publisher); + const authorised = isUserMemberOfTeamByName(requestingUser, dataRequestSchema.publisher); if (!authorised) { return res.status(401).json({ @@ -69,7 +74,7 @@ const authorizePostRequest = async (req, res, next) => { }); } - req.body.datarequestschema = datarequestschema; + req.body.dataRequestSchema = dataRequestSchema; next(); }; @@ -86,17 +91,10 @@ router.get( (req, res) => questionbankController.getQuestionbank(req, res) ); -// @route GET /api/v1/questionbanks -// @desc Returns a collection of questionbanks based on supplied query parameters -// @access Public -router.get('/', logger.logRequestMiddleware({ logCategory, action: 'Viewed questionbanks data' }), (req, res) => - questionbankController.getQuestionbanks(req, res) -); - // @route POST /api/v1/questionbanks // @desc Activate a draft schema creating a jsonSchema from masterSchema // @access Public -router.post('/schemaId', passport.authenticate('jwt'), validatePostRequest, authorizePostRequest, (req, res) => +router.post('/:schemaId', passport.authenticate('jwt'), validatePostRequest, authorizePostRequest, (req, res) => questionbankController.publishSchema(req, res) ); diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 45a876ee..f9e567bb 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -127,23 +127,27 @@ export default class QuestionbankService { const { guidance, questionStatus } = schema; masterSchema.questionSets.forEach((questionSet, questionSetIndex) => { - questionSet.questions.forEach((question, questionIndex) => { + let questionsArray = masterSchema.questionSets[questionSetIndex].questions; + questionSet.questions.forEach(question => { if (questionStatus[question.questionId] === 0) { - delete masterSchema.questionSets[questionSetIndex].questions[questionIndex]; + questionsArray = questionsArray.filter(q => q.questionId !== question.questionId); } else { - if (has(guidance, question.questionId)) question.guidance = guidance[question.questionId]; - delete masterSchema.questionSets[questionSetIndex].questions[questionIndex].lockedQuestion; - delete masterSchema.questionSets[questionSetIndex].questions[questionIndex].defaultQuestion; + if (has(guidance, question.questionId)) { + question.guidance = guidance[question.questionId]; + } + delete question.lockedQuestion; + delete question.defaultQuestion; } }); + masterSchema.questionSets[questionSetIndex].questions = questionsArray; }); const jsonSchema = masterSchema; - const publishedSchema = await this.dataRequestRepository.updateApplicationFormSchemaById(schema._id, { jsonSchema }); + const publishedSchema = await this.dataRequestRepository.updateApplicationFormSchemaById(schema._id, { jsonSchema, status: 'active' }); //if its not already a 5 safes publisher then set the flags to true on the publisher and also on the datasets - const publisher = await this.publisherService.getPublisher(schema.publisher); + const publisher = await this.publisherService.getPublisher(schema.publisher, { lean: true }); if (!has(publisher, 'uses5Safes')) { await this.publisherService.update(publisher._id, { allowsMessaging: true, @@ -152,7 +156,7 @@ export default class QuestionbankService { uses5Safes: true, }); - await this.datasetService.updateMany({ 'datasetfields.publisher': schema.publisher }, { is5Safes: false }); + await this.datasetService.updateMany({ 'datasetfields.publisher': schema.publisher }, { is5Safes: true }); } return publishedSchema; From e1a081e4eeb6f533f899b2fa6fcc8167843bd268 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Fri, 10 Dec 2021 14:13:36 +0000 Subject: [PATCH 017/389] remove comments --- .../__tests__/questionbank.service.test.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index 5ce9d613..d3e7c0aa 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -87,19 +87,3 @@ describe('Question Bank Service', function () { expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); }); }); - -describe('Question Bank Service, schema creation', function () { - // Pull the master schema - // Get the question status and guidance - // Loops through the question sets on master schema - // If question status is off then remove the question - // If question is on check the if guidance is changed and update the guidance - // Remove the extra options that are not needed by the final version of a schema - // Locked question option - // Default question option - // Save final schema as a new version along with question status and guidance objects - // If first time for 5 safes - // Set the flags that are needed for 5 safes to be functional - - it('creation of a new data request schema'); -}); From b8d3b7d9ef5283b4f9eea16aef3668b3bb32ae49 Mon Sep 17 00:00:00 2001 From: massimocianfroccaPA Date: Fri, 10 Dec 2021 15:29:41 +0000 Subject: [PATCH 018/389] mode utilities function away from route --- src/resources/auth/utils.js | 14 +++++++++++++- src/resources/questionbank/questionbank.route.js | 13 ++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 97cb301b..b8f9f024 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -3,7 +3,7 @@ import passport from 'passport'; import jwt from 'jsonwebtoken'; import { to } from 'await-to-js'; import Url from 'url'; -import { isEmpty } from 'lodash'; +import { isEmpty, isNull } from 'lodash'; import queryString from 'query-string'; import { ROLES } from '../user/user.roles'; @@ -202,6 +202,16 @@ const loginAndSignToken = (req, res, next) => { }); }; +function isUserMemberOfTeamById(user, teamId) { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); +} + +function isUserMemberOfTeamByName(user, publisherName) { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher.name === publisherName); +} + export { setup, signToken, @@ -213,4 +223,6 @@ export { getTeams, catchLoginErrorAndRedirect, loginAndSignToken, + isUserMemberOfTeamById, + isUserMemberOfTeamByName, }; diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index 83edd96b..e3a44a3a 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -1,25 +1,16 @@ import express from 'express'; import passport from 'passport'; -import { isUndefined, isNull, isEmpty } from 'lodash'; +import { isUndefined, isEmpty } from 'lodash'; import QuestionbankController from './questionbank.controller'; import { questionbankService } from './dependency'; import { datarequestschemaService } from './../datarequest/schema/dependency'; import { logger } from '../utilities/logger'; +import { isUserMemberOfTeamById, isUserMemberOfTeamByName } from '../auth/utils'; const router = express.Router(); const questionbankController = new QuestionbankController(questionbankService); const logCategory = 'questionbank'; -function isUserMemberOfTeamById(user, teamId) { - let { teams } = user; - return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); -} - -function isUserMemberOfTeamByName(user, publisherName) { - let { teams } = user; - return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher.name === publisherName); -} - const validateViewRequest = (req, res, next) => { const { publisherId } = req.params; From 32f6489bdcf41329786d05c006c410edb86d885b Mon Sep 17 00:00:00 2001 From: Paul McCafferty <> Date: Mon, 13 Dec 2021 10:24:51 +0000 Subject: [PATCH 019/389] Updates to API --- src/resources/questionbank/questionbank.service.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index f9e567bb..41859310 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -21,7 +21,8 @@ export default class QuestionbankService { //create the questionStatus from the master schema masterSchema.questionSets.forEach(questionSet => { questionSet.questions.forEach(question => { - questionStatus[question.questionId] = question.defaultQuestion; + if (question.lockedQuestion === 1) questionStatus[question.questionId] = 2; + else questionStatus[question.questionId] = question.defaultQuestion; }); }); @@ -148,7 +149,7 @@ export default class QuestionbankService { //if its not already a 5 safes publisher then set the flags to true on the publisher and also on the datasets const publisher = await this.publisherService.getPublisher(schema.publisher, { lean: true }); - if (!has(publisher, 'uses5Safes')) { + if (!has(publisher, 'uses5Safes') || publisher.uses5Safes === false) { await this.publisherService.update(publisher._id, { allowsMessaging: true, workflowEnabled: true, @@ -178,7 +179,8 @@ export default class QuestionbankService { masterSchema.questionSets.forEach(questionSet => { questionSet.questions.forEach(question => { if (!has(publisherSchema.questionStatus, question.questionId)) { - questionStatus[question.questionId] = question.defaultQuestion; + if (question.lockedQuestion === 1) questionStatus[question.questionId] = 2; + else questionStatus[question.questionId] = question.defaultQuestion; newQuestionsAdded = true; } }); From 8e5a0d8ef107081a4f8d37de123060fa80bee6ed Mon Sep 17 00:00:00 2001 From: Paul McCafferty <> Date: Mon, 13 Dec 2021 21:48:57 +0000 Subject: [PATCH 020/389] Updates to question bank --- src/resources/questionbank/questionbank.controller.js | 2 +- src/resources/questionbank/questionbank.service.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index f691d947..cd1033f5 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -54,7 +54,7 @@ export default class QuestionbankController extends Controller { try { const { dataRequestSchema } = req.body; - const newRequestSchema = await this.questionbankService.publishSchema(dataRequestSchema); + const newRequestSchema = await this.questionbankService.publishSchema(dataRequestSchema, req.user.id); return res.status(200).json({ success: true, result: newRequestSchema }); } catch (err) { diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 41859310..3e0d7f81 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -122,7 +122,7 @@ export default class QuestionbankService { } } - async publishSchema(schema) { + async publishSchema(schema, userId) { const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); const masterSchema = global.masterSchema; const { guidance, questionStatus } = schema; @@ -160,6 +160,11 @@ export default class QuestionbankService { await this.datasetService.updateMany({ 'datasetfields.publisher': schema.publisher }, { is5Safes: true }); } + await this.publisherService.update(publisher._id, { + applicationFormUpdatedOn: Date.now(), + applicationFormUpdatedBy: userId, + }); + return publishedSchema; } From a7527b07403d7211b68c7a8e60cac4c268162da5 Mon Sep 17 00:00:00 2001 From: CiaraWardPA Date: Mon, 6 Dec 2021 13:26:37 +0000 Subject: [PATCH 021/389] IG-2560 update /api/v1/users endpoint to return collaborators at the top of the list of all users --- src/resources/user/user.route.js | 61 ++---------- src/resources/user/user.service.js | 146 +++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 55 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 6397acea..94bcaaf7 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -6,7 +6,7 @@ import { UserModel } from './user.model'; import { Data } from '../tool/data.model'; import helper from '../utilities/helper.util'; import { ROLES } from './user.roles'; -import { setCohortDiscoveryAccess } from './user.service'; +import { setCohortDiscoveryAccess, getUsers } from './user.service'; import { upperCase } from 'lodash'; //import { createServiceAccount } from './user.repository'; @@ -29,62 +29,13 @@ router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async // @desc get all // @access Private router.get('/', passport.authenticate('jwt'), async (req, res) => { - var q = Data.aggregate([ - // Find all tools with type of person - { $match: { type: 'person' } }, - // Perform lookup to users - { - $lookup: { - from: 'users', - localField: 'id', - foreignField: 'id', - as: 'user', - }, - }, - // select fields to use - { - $project: { - _id: '$user._id', - id: 1, - firstname: 1, - lastname: 1, - orcid: { - $cond: [ - { - $eq: [true, '$showOrcid'], - }, - '$orcid', - '$$REMOVE', - ], - }, - bio: { - $cond: [ - { - $eq: [true, '$showBio'], - }, - '$bio', - '$$REMOVE', - ], - }, - email: '$user.email', - }, - }, - ]); - - q.exec((err, data) => { - if (err) { + await getUsers(req.user.id) + .then(response => { + return res.json({ success: true, data: response }); + }) + .catch(err => { return new Error({ success: false, error: err }); - } - - const users = []; - data.map(dat => { - let { _id, id, firstname, lastname, orcid = '', bio = '', email = '' } = dat; - if (email.length !== 0) email = helper.censorEmail(email[0]); - users.push({ _id, id, orcid, name: `${firstname} ${lastname}`, bio, email }); }); - - return res.json({ success: true, data: users }); - }); }); // @router PATCH /api/v1/users/advancedSearch/terms/:id diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 71252049..dde0eed9 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -1,5 +1,10 @@ import emailGeneratorUtil from '../utilities/emailGenerator.util'; import { UserModel } from './user.model'; +import { Data } from '../tool/data.model'; +import helper from '../utilities/helper.util'; +import { Cohort } from '../cohort/cohort.model'; +import { Collections } from '../collections/collections.model'; +import { DataRequestModel } from '../datarequest/datarequest.model'; export async function createUser({ firstname, lastname, email, providerId, provider, role }) { return new Promise(async resolve => { @@ -77,3 +82,144 @@ export async function setCohortDiscoveryAccess(id, roles) { return resolve(updatedUser); }); } + +// Gets all of the logged in users collaborators +export async function getUsersCollaborators(currentUserId) { + let collaborators = []; + + // Get all collaborators from collections + let collaboratorsCollections = await Collections.find({ authors: currentUserId }, { _id: 0, authors: 1 }); + await populateCollaborators(collaboratorsCollections, 'authors', collaborators, currentUserId); + + // Get all collaborators from cohorts + let collaboratorsCohorts = await Cohort.find({ uploaders: currentUserId }, { _id: 0, uploaders: 1 }); + await populateCollaborators(collaboratorsCohorts, 'uploaders', collaborators, currentUserId); + + // Get all collaborators from tools and papers (data collection) + let collaboratorsTools = await Data.find({ authors: currentUserId }, { _id: 0, authors: 1 }); + await populateCollaborators(collaboratorsTools, 'authors', collaborators, currentUserId); + + // Get all collaborators from DARs + let collaboratorsDARs = await DataRequestModel.find( + { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }, + { _id: 0, authorIds: 1, userId: 1 } + ); + await populateCollaborators(collaboratorsDARs, 'authorIds', collaborators, currentUserId); + + // Strip out duplicate collaborators, add a count + let uniqueCollaborators = new Map(); + + for (const collaborator of collaborators) { + if (uniqueCollaborators.has(collaborator)) { + let incrementedValue = uniqueCollaborators.get(collaborator) + 1; + uniqueCollaborators.set(collaborator, incrementedValue); + } else { + uniqueCollaborators.set(collaborator, 1); + } + } + + return uniqueCollaborators; +} + +async function populateCollaborators(collaboratorsEntity, items, collaborators, currentUserId) { + for (const collaborator of collaboratorsEntity) { + if (items === 'authorIds' && collaborator.userId !== currentUserId) { + collaborators.push(collaborator.userId); + } + + for (const item of collaborator[items]) { + if (item !== currentUserId) { + collaborators.push(item); + } + } + } + + return collaborators; +} + +export async function getUsers(currentUserId) { + // Get the users collaborators + let usersCollaborators = await getUsersCollaborators(currentUserId); + + // Get the whole list of users + var q = Data.aggregate([ + // Find all tools with type of person + { $match: { type: 'person' } }, + // Perform lookup to users + { + $lookup: { + from: 'users', + localField: 'id', + foreignField: 'id', + as: 'user', + }, + }, + // select fields to use + { + $project: { + _id: '$user._id', + id: 1, + firstname: 1, + lastname: 1, + orcid: { + $cond: [ + { + $eq: [true, '$showOrcid'], + }, + '$orcid', + '$$REMOVE', + ], + }, + bio: { + $cond: [ + { + $eq: [true, '$showBio'], + }, + '$bio', + '$$REMOVE', + ], + }, + email: '$user.email', + }, + }, + ]); + + return new Promise((resolve, reject) => { + q.exec((err, data) => { + if (err) { + return err; + } + + const users = []; + data.map(dat => { + let { _id, id, firstname, lastname, orcid = '', bio = '', email = '' } = dat; + if (email.length !== 0) email = helper.censorEmail(email[0]); + users.push({ _id, id, orcid, name: `${firstname} ${lastname}`, bio, email }); + }); + + let collaborators = []; + let nonCollaboratorUsers = []; + + // Pull all non collaborators from users + nonCollaboratorUsers = users.filter(user => !usersCollaborators.has(user.id)); + + // Pull all collaborators from users, add count to sort by + for (const user of users) { + usersCollaborators.forEach((count, collaboratorId) => { + if (user.id === collaboratorId) { + collaborators.push({ user: user, count: count }); + } + }); + } + + collaborators.sort((a, b) => b.count - a.count); + + // Remove count after collaborators are sorted + let collaboratorUsers = collaborators.map(collaborator => { + return collaborator.user; + }); + + resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + }); + }); +} From 6b34cc42845b7b2d1c90ff69adb49ca68794087b Mon Sep 17 00:00:00 2001 From: CiaraWardPA Date: Mon, 6 Dec 2021 14:40:03 +0000 Subject: [PATCH 022/389] IG-2560 fix LGTM errors --- src/resources/user/user.route.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 94bcaaf7..83992502 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -3,8 +3,6 @@ import passport from 'passport'; import { utils } from '../auth'; import { UserModel } from './user.model'; -import { Data } from '../tool/data.model'; -import helper from '../utilities/helper.util'; import { ROLES } from './user.roles'; import { setCohortDiscoveryAccess, getUsers } from './user.service'; import { upperCase } from 'lodash'; From ab96017978424566fa4fefa3f9107042913e574c Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 17 Feb 2022 09:57:31 +0000 Subject: [PATCH 023/389] fixed archive auto DUR --- src/resources/dataUseRegister/dataUseRegister.route.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.route.js b/src/resources/dataUseRegister/dataUseRegister.route.js index 5b63ad54..f5f6c27d 100644 --- a/src/resources/dataUseRegister/dataUseRegister.route.js +++ b/src/resources/dataUseRegister/dataUseRegister.route.js @@ -95,13 +95,13 @@ const authorizeUpdate = async (req, res, next) => { } if (!dataUseRegister.manualUpload) { - if (!isEqual(projectIdText, dataUseRegister.projectIdText)) + if (projectIdText && !isEqual(projectIdText, dataUseRegister.projectIdText)) return res.status(401).json({ success: false, message: 'You are not authorised to update the project ID of an automatic data use register', }); - if (!isEqual(datasetTitles, dataUseRegister.datasetTitles)) + if (datasetTitles && !isEqual(datasetTitles, dataUseRegister.datasetTitles)) return res.status(401).json({ success: false, message: 'You are not authorised to update the datasets of an automatic data use register', From cf8a34c00e0959fcf196ebebce791499a6a66c7a Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 2 Mar 2022 10:22:13 +0000 Subject: [PATCH 024/389] move DUR middleware to middleware folder --- .../datasetonboarding.middleware.test.js | 1 - src/middlewares/dataUseRegister.middleware.js | 120 +++++++++++++++++ src/middlewares/index.js | 9 +- .../dataUseRegister/dataUseRegister.route.js | 124 +----------------- 4 files changed, 131 insertions(+), 123 deletions(-) create mode 100644 src/middlewares/dataUseRegister.middleware.js diff --git a/src/middlewares/__tests__/datasetonboarding.middleware.test.js b/src/middlewares/__tests__/datasetonboarding.middleware.test.js index 2d073425..bbea2d0f 100644 --- a/src/middlewares/__tests__/datasetonboarding.middleware.test.js +++ b/src/middlewares/__tests__/datasetonboarding.middleware.test.js @@ -1,7 +1,6 @@ import { authoriseUserForPublisher, validateSearchParameters } from '../datasetonboarding.middleware'; import { UserModel } from '../../resources/user/user.model'; import constants from '../../resources/utilities/constants.util'; -import { testing } from 'googleapis/build/src/apis/testing'; describe('Testing the datasetonboarding middleware', () => { const mockedRequest = () => { diff --git a/src/middlewares/dataUseRegister.middleware.js b/src/middlewares/dataUseRegister.middleware.js new file mode 100644 index 00000000..018d0a97 --- /dev/null +++ b/src/middlewares/dataUseRegister.middleware.js @@ -0,0 +1,120 @@ +import { isEmpty, isNull, isEqual } from 'lodash'; + +import constants from '../resources/utilities/constants.util'; + +const _isUserMemberOfTeam = (user, teamId) => { + let { teams } = user; + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); +}; + +const _isUserDataUseAdmin = user => { + 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 }; + }); + } + + return teams + .filter(team => team.type === constants.teamTypes.ADMIN) + .some(team => team.roles.includes(constants.roleTypes.ADMIN_DATA_USE)); +}; + +const validateUpdateRequest = (req, res, next) => { + const { id } = req.params; + + if (!id) { + return res.status(400).json({ + success: false, + message: 'You must provide a data user register identifier', + }); + } + + next(); +}; + +const validateUploadRequest = (req, res, next) => { + const { teamId, dataUses } = req.body; + let errors = []; + + if (!teamId) { + errors.push('You must provide the custodian team identifier to associate the data uses to'); + } + + if (!dataUses || isEmpty(dataUses)) { + errors.push('You must provide data uses to upload'); + } + + if (!isEmpty(errors)) { + return res.status(400).json({ + success: false, + message: errors.join(', '), + }); + } + + next(); +}; + +const authorizeUpdate = async (req, res, next) => { + const requestingUser = req.user; + const { id } = req.params; + const { projectIdText, datasetTitles } = req.body; + + const dataUseRegister = await dataUseRegisterService.getDataUseRegister(id); + + if (!dataUseRegister) { + return res.status(404).json({ + success: false, + message: 'The requested data use register entry could not be found', + }); + } + + const { publisher } = dataUseRegister; + const authorised = _isUserDataUseAdmin(requestingUser) || _isUserMemberOfTeam(requestingUser, publisher._id); + if (!authorised) { + return res.status(401).json({ + success: false, + message: 'You are not authorised to perform this action', + }); + } + + if (!dataUseRegister.manualUpload) { + if (projectIdText && !isEqual(projectIdText, dataUseRegister.projectIdText)) + return res.status(401).json({ + success: false, + message: 'You are not authorised to update the project ID of an automatic data use register', + }); + + if (datasetTitles && !isEqual(datasetTitles, dataUseRegister.datasetTitles)) + return res.status(401).json({ + success: false, + message: 'You are not authorised to update the datasets of an automatic data use register', + }); + } + + next(); +}; + +const authorizeUpload = async (req, res, next) => { + const requestingUser = req.user; + const { teamId } = req.body; + + const authorised = _isUserDataUseAdmin(requestingUser) || _isUserMemberOfTeam(requestingUser, teamId); + + if (!authorised) { + return res.status(401).json({ + success: false, + message: 'You are not authorised to perform this action', + }); + } + + next(); +}; + +export { validateUpdateRequest, validateUploadRequest, authorizeUpdate, authorizeUpload }; diff --git a/src/middlewares/index.js b/src/middlewares/index.js index a49ce158..74cdab05 100644 --- a/src/middlewares/index.js +++ b/src/middlewares/index.js @@ -11,6 +11,7 @@ import { import checkInputMiddleware from './checkInputMiddleware'; import checkMinLengthMiddleware from './checkMinLengthMiddleware'; import checkStringMiddleware from './checkStringMiddleware'; +import { validateUpdateRequest, validateUploadRequest, authorizeUpdate, authorizeUpload } from './dataUseRegister.middleware'; export { checkIDMiddleware, @@ -21,8 +22,12 @@ export { validateDeleteRequest, authoriseDelete, checkInputMiddleware, - checkMinLengthMiddleware, - checkStringMiddleware, + checkMinLengthMiddleware, + checkStringMiddleware, authoriseUserForPublisher, validateSearchParameters, + validateUpdateRequest, + validateUploadRequest, + authorizeUpdate, + authorizeUpload, }; diff --git a/src/resources/dataUseRegister/dataUseRegister.route.js b/src/resources/dataUseRegister/dataUseRegister.route.js index f5f6c27d..a2ff75cf 100644 --- a/src/resources/dataUseRegister/dataUseRegister.route.js +++ b/src/resources/dataUseRegister/dataUseRegister.route.js @@ -1,132 +1,16 @@ import express from 'express'; -import DataUseRegisterController from './dataUseRegister.controller'; +import passport from 'passport'; +import { logger } from '../utilities/logger'; import { dataUseRegisterService } from './dependency'; import { activityLogService } from '../activitylog/dependency'; -import { logger } from '../utilities/logger'; -import passport from 'passport'; -import constants from './../utilities/constants.util'; -import { isEmpty, isNull, isEqual } from 'lodash'; +import DataUseRegisterController from './dataUseRegister.controller'; +import { validateUpdateRequest, authorizeUpdate, validateUploadRequest, authorizeUpload } from '../../middlewares'; const router = express.Router(); const dataUseRegisterController = new DataUseRegisterController(dataUseRegisterService, activityLogService); const logCategory = 'dataUseRegister'; -function isUserMemberOfTeam(user, teamId) { - let { teams } = user; - return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); -} - -function isUserDataUseAdmin(user) { - 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 }; - }); - } - - return teams - .filter(team => team.type === constants.teamTypes.ADMIN) - .some(team => team.roles.includes(constants.roleTypes.ADMIN_DATA_USE)); -} - -const validateUpdateRequest = (req, res, next) => { - const { id } = req.params; - - if (!id) { - return res.status(400).json({ - success: false, - message: 'You must provide a data user register identifier', - }); - } - - next(); -}; - -const validateUploadRequest = (req, res, next) => { - const { teamId, dataUses } = req.body; - let errors = []; - - if (!teamId) { - errors.push('You must provide the custodian team identifier to associate the data uses to'); - } - - if (!dataUses || isEmpty(dataUses)) { - errors.push('You must provide data uses to upload'); - } - - if (!isEmpty(errors)) { - return res.status(400).json({ - success: false, - message: errors.join(', '), - }); - } - - next(); -}; - -const authorizeUpdate = async (req, res, next) => { - const requestingUser = req.user; - const { id } = req.params; - const { projectIdText, datasetTitles } = req.body; - - const dataUseRegister = await dataUseRegisterService.getDataUseRegister(id); - - if (!dataUseRegister) { - return res.status(404).json({ - success: false, - message: 'The requested data use register entry could not be found', - }); - } - - const { publisher } = dataUseRegister; - const authorised = isUserDataUseAdmin(requestingUser) || isUserMemberOfTeam(requestingUser, publisher._id); - if (!authorised) { - return res.status(401).json({ - success: false, - message: 'You are not authorised to perform this action', - }); - } - - if (!dataUseRegister.manualUpload) { - if (projectIdText && !isEqual(projectIdText, dataUseRegister.projectIdText)) - return res.status(401).json({ - success: false, - message: 'You are not authorised to update the project ID of an automatic data use register', - }); - - if (datasetTitles && !isEqual(datasetTitles, dataUseRegister.datasetTitles)) - return res.status(401).json({ - success: false, - message: 'You are not authorised to update the datasets of an automatic data use register', - }); - } - - next(); -}; - -const authorizeUpload = async (req, res, next) => { - const requestingUser = req.user; - const { teamId } = req.body; - - const authorised = isUserDataUseAdmin(requestingUser) || isUserMemberOfTeam(requestingUser, teamId); - - if (!authorised) { - return res.status(401).json({ - success: false, - message: 'You are not authorised to perform this action', - }); - } - - next(); -}; - router.get('/search', logger.logRequestMiddleware({ logCategory, action: 'Search uploaded data uses' }), (req, res) => dataUseRegisterController.searchDataUseRegisters(req, res) ); From b35a592ddb5a627d29cf38d9df88a0e2b698c53e Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 2 Mar 2022 11:37:32 +0000 Subject: [PATCH 025/389] add DUR middleware test --- .../dataUseRegister.middleware.test.js | 272 ++++++++++++++++++ src/middlewares/dataUseRegister.middleware.js | 3 +- 2 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/middlewares/__tests__/dataUseRegister.middleware.test.js diff --git a/src/middlewares/__tests__/dataUseRegister.middleware.test.js b/src/middlewares/__tests__/dataUseRegister.middleware.test.js new file mode 100644 index 00000000..430d3a99 --- /dev/null +++ b/src/middlewares/__tests__/dataUseRegister.middleware.test.js @@ -0,0 +1,272 @@ +import sinon from 'sinon'; + +import { dataUseRegisterService } from '../../resources/dataUseRegister/dependency'; +import { validateUpdateRequest, validateUploadRequest, authorizeUpdate, authorizeUpload } from '../dataUseRegister.middleware'; + +afterEach(function () { + sinon.restore(); +}); + +describe('Testing the dataUserRegister middleware', () => { + const mockedRequest = () => { + const req = {}; + req.params = jest.fn().mockReturnValue(req); + req.body = jest.fn().mockReturnValue(req); + return req; + }; + + const mockedResponse = () => { + const res = {}; + res.status = jest.fn().mockReturnValue(res); + res.json = jest.fn().mockReturnValue(res); + return res; + }; + + describe('Testing the validateUpdateRequest middleware', () => { + it('it should invoke next() if a valid ID is passed in the request', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.params.id = 'mockID'; + + validateUpdateRequest(req, res, nextFunction); + + expect(nextFunction.mock.calls.length).toBe(1); + }); + + it('it should return the appropriate 400 error if no ID is given in the request', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + validateUpdateRequest(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You must provide a data user register identifier', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(400); + }); + }); + + describe('Testing the validateUploadRequest middleware', () => { + it('it should invoke next() if a valid teamID and dataUses array are supplied in the request', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body.teamId = 'testID'; + req.body.dataUses = ['dataUse']; + + validateUploadRequest(req, res, nextFunction); + + expect(nextFunction.mock.calls.length).toBe(1); + }); + + it('it should give an appropriate error if no teamID is given in the request', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body.dataUses = ['dataUse']; + + validateUploadRequest(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You must provide the custodian team identifier to associate the data uses to', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('it should give an appropriate error if no dataUses are given in the request', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body.teamId = 'testID'; + + validateUploadRequest(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You must provide data uses to upload', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(400); + }); + }); + + describe('Testing the authorizeUpdate middleware', () => { + it('it should return a 404 if no data use can be found for a given ID', async () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + sinon.stub(dataUseRegisterService, 'getDataUseRegister'); + + await authorizeUpdate(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'The requested data use register entry could not be found', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(404); + }); + + it('it should return a 401 if user not authorised to update DUR', async () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.user = { + _id: 'testUser', + teams: [ + { publisher: { _id: 'testPublisher' }, type: 'NOT_ADMIN_TEAM', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }, + ], + }; + + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ publisher: 'testPublisher' }); + + await authorizeUpdate(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You are not authorised to perform this action', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(401); + }); + + it('it should return a 401 if the projectID text is mismatched', async () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body = { + projectIdText: 'notAMatch', + }; + + req.user = { + _id: 'testUser', + teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + }; + + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ projectIdText: 'testIdText' }); + + await authorizeUpdate(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You are not authorised to update the project ID of an automatic data use register', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(401); + }); + + it('it should return a 401 if the datasetTitles is mismatched', async () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body = { + datasetTitles: 'notAMatch', + }; + + req.user = { + _id: 'testUser', + teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + }; + + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'datasetTitles' }); + + await authorizeUpdate(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You are not authorised to update the datasets of an automatic data use register', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(401); + }); + + it('it should invoke next if all conditions are satisfied', async () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.body = { + datasetTitles: 'match', + projectIdText: 'match', + }; + + req.user = { + _id: 'testUser', + teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + }; + + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'match', projectIdText: 'match' }); + + await authorizeUpdate(req, res, nextFunction); + + expect(nextFunction.mock.calls.length).toBe(1); + }); + }); + + describe('Testing the authorizeUpload middleware', () => { + it('It should return 401 is user is not authorised', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.user = { + _id: 'testUser', + teams: [{ publisher: { _id: 'testPublisher' }, type: 'NotAdmin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + }; + + authorizeUpload(req, res, nextFunction); + + const expectedResponse = { + success: false, + message: 'You are not authorised to perform this action', + }; + + expect(nextFunction.mock.calls.length).toBe(0); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(res.status).toHaveBeenCalledWith(401); + }); + + it('It should invoke next() if user is authorised', () => { + let req = mockedRequest(); + let res = mockedResponse(); + const nextFunction = jest.fn(); + + req.user = { + _id: 'testUser', + teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + }; + + authorizeUpload(req, res, nextFunction); + + expect(nextFunction.mock.calls.length).toBe(1); + }); + }); +}); diff --git a/src/middlewares/dataUseRegister.middleware.js b/src/middlewares/dataUseRegister.middleware.js index 018d0a97..a6292dea 100644 --- a/src/middlewares/dataUseRegister.middleware.js +++ b/src/middlewares/dataUseRegister.middleware.js @@ -1,10 +1,11 @@ import { isEmpty, isNull, isEqual } from 'lodash'; import constants from '../resources/utilities/constants.util'; +import { dataUseRegisterService } from '../resources/dataUseRegister/dependency'; const _isUserMemberOfTeam = (user, teamId) => { let { teams } = user; - return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id === teamId); }; const _isUserDataUseAdmin = user => { From 97bac3850d93dc0a4143274b3b8ff8549d1a4d8d Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 2 Mar 2022 12:27:12 +0000 Subject: [PATCH 026/389] update tests --- .../__tests__/dataUseRegister.middleware.test.js | 12 +++++++++--- src/middlewares/dataUseRegister.middleware.js | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/middlewares/__tests__/dataUseRegister.middleware.test.js b/src/middlewares/__tests__/dataUseRegister.middleware.test.js index 430d3a99..e05bc752 100644 --- a/src/middlewares/__tests__/dataUseRegister.middleware.test.js +++ b/src/middlewares/__tests__/dataUseRegister.middleware.test.js @@ -134,7 +134,11 @@ describe('Testing the dataUserRegister middleware', () => { req.user = { _id: 'testUser', teams: [ - { publisher: { _id: 'testPublisher' }, type: 'NOT_ADMIN_TEAM', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }, + { + publisher: { _id: { equals: jest.fn() } }, + type: 'NOT_ADMIN_TEAM', + members: [{ memberid: 'testUser', roles: 'admin_data_use' }], + }, ], }; @@ -232,14 +236,16 @@ describe('Testing the dataUserRegister middleware', () => { }); describe('Testing the authorizeUpload middleware', () => { - it('It should return 401 is user is not authorised', () => { + it('It should return 401 if user is not authorised', () => { let req = mockedRequest(); let res = mockedResponse(); const nextFunction = jest.fn(); req.user = { _id: 'testUser', - teams: [{ publisher: { _id: 'testPublisher' }, type: 'NotAdmin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], + teams: [ + { publisher: { _id: { equals: jest.fn() } }, type: 'NotAdmin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }, + ], }; authorizeUpload(req, res, nextFunction); diff --git a/src/middlewares/dataUseRegister.middleware.js b/src/middlewares/dataUseRegister.middleware.js index a6292dea..7d3e6fa5 100644 --- a/src/middlewares/dataUseRegister.middleware.js +++ b/src/middlewares/dataUseRegister.middleware.js @@ -5,7 +5,7 @@ import { dataUseRegisterService } from '../resources/dataUseRegister/dependency' const _isUserMemberOfTeam = (user, teamId) => { let { teams } = user; - return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id === teamId); + return teams.filter(team => !isNull(team.publisher)).some(team => team.publisher._id.equals(teamId)); }; const _isUserDataUseAdmin = user => { From c2facca7bcb21df0c8964c83030c2c970bcc9b45 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 2 Mar 2022 13:27:43 +0000 Subject: [PATCH 027/389] update dataset onboarding --- .../datasetonboarding.controller.js | 522 ++++++++---------- src/services/httpClient/httpClient.js | 73 +++ 2 files changed, 295 insertions(+), 300 deletions(-) create mode 100644 src/services/httpClient/httpClient.js diff --git a/src/controllers/datasetonboarding.controller.js b/src/controllers/datasetonboarding.controller.js index 759a18ae..1d6f96ac 100644 --- a/src/controllers/datasetonboarding.controller.js +++ b/src/controllers/datasetonboarding.controller.js @@ -12,6 +12,7 @@ import { filtersService } from '../resources/filters/dependency'; import datasetonboardingUtil from '../utils/datasetonboarding.util'; 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'; @@ -217,325 +218,246 @@ export default class DatasetOnboardingController { }; changeDatasetVersionStatus = async (req, res) => { - try { - // 1. Id is the _id object in MongoDb not the generated id or dataset Id - // 2. Get the userId - const id = req.params.id || null; - let { firstname, lastname } = req.user; - let { applicationStatus, applicationStatusDesc = '' } = req.body; + const id = req.params.id || null; + if (!id) { + return res.status(404).json({ + status: 'error', + message: 'Dataset _id could not be found.' + }); + } - if (!id) return res.status(404).json({ status: 'error', message: 'Dataset _id could not be found.' }); + let { firstname, lastname } = req.user; + let { applicationStatus, applicationStatusDesc = '' } = req.body; - // 3. Check user type and authentication to submit application - let { authorised, userType } = await datasetonboardingUtil.getUserPermissionsForDataset(id, req.user); - if (!authorised) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } + let { authorised, userType } = await datasetonboardingUtil.getUserPermissionsForDataset(id, req.user); + if (!authorised) { + return res.status(401).json({ + status: 'failure', + message: 'Unauthorised' + }); + } - if (applicationStatus === 'approved') { - if (userType !== constants.userTypes.ADMIN) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } + try { + let metadataCatalogueLink = process.env.MDC_Config_HDRUK_metadataUrl || 'https://modelcatalogue.cs.ox.ac.uk/hdruk-preprod'; + const loginDetails = { + username: process.env.MDC_Config_HDRUK_username || '', + password: process.env.MDC_Config_HDRUK_password || '', + }; + let updatedDataset = null; + let dataset = null; + let activityLogStatus = null; + + const _httpClient = new HttpClient(); + switch(applicationStatus) { + case 'approved': + if (userType !== constants.userTypes.ADMIN) { + return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + } - let dataset = await Data.findOne({ _id: id }); - - if (!dataset) return res.status(404).json({ status: 'error', message: 'Dataset could not be found.' }); - - dataset.questionAnswers = JSON.parse(dataset.questionAnswers); - const publisherData = await PublisherModel.find({ _id: dataset.datasetv2.summary.publisher.identifier }).lean(); - - //1. create new version on MDC with version number and take datasetid and store - let metadataCatalogueLink = process.env.MDC_Config_HDRUK_metadataUrl || 'https://modelcatalogue.cs.ox.ac.uk/hdruk-preprod'; - const loginDetails = { - username: process.env.MDC_Config_HDRUK_username || '', - password: process.env.MDC_Config_HDRUK_password || '', - }; - - await axios - .post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { - withCredentials: true, - timeout: 5000, - }) - .then(async session => { - axios.defaults.headers.Cookie = session.headers['set-cookie'][0]; // get cookie from request - - let jsonData = JSON.stringify(await datasetonboardingUtil.buildJSONFile(dataset)); - fs.writeFileSync(__dirname + `/datasetfiles/${dataset._id}.json`, jsonData); - - var data = new FormData(); - data.append('folderId', publisherData[0].mdcFolderId); - data.append('importFile', fs.createReadStream(__dirname + `/datasetfiles/${dataset._id}.json`)); - data.append('finalised', 'false'); - data.append('importAsNewDocumentationVersion', 'true'); - - await axios - .post( - metadataCatalogueLink + '/api/dataModels/import/ox.softeng.metadatacatalogue.core.spi.json/JsonImporterService/1.1', - data, - { - withCredentials: true, - timeout: 60000, - headers: { - ...data.getHeaders(), - }, - } - ) - .then(async newDatasetVersion => { - let newDatasetVersionId = newDatasetVersion.data.items[0].id; - fs.unlinkSync(__dirname + `/datasetfiles/${dataset._id}.json`); - - const updatedDatasetDetails = { - documentationVersion: dataset.datasetVersion, - }; - - await axios - .put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}`, updatedDatasetDetails, { - withCredentials: true, - timeout: 20000, - }) - .catch(err => { - console.error('Error when trying to update the version number on the MDC - ' + err.message); - }); - - await axios - .put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}/finalise`, { - withCredentials: true, - timeout: 20000, - }) - .catch(err => { - console.error('Error when trying to finalise the dataset on the MDC - ' + err.message); - }); - - // Adding to DB - let datasetv2Object = await datasetonboardingUtil.buildv2Object(dataset, newDatasetVersionId); - - let previousDataset = await Data.findOneAndUpdate({ pid: dataset.pid, activeflag: 'active' }, { activeflag: 'archive' }); - let previousCounter = 0; - let previousDiscourseTopicId = 0; - if (previousDataset) previousCounter = previousDataset.counter || 0; - if (previousDataset) previousDiscourseTopicId = previousDataset.discourseTopicId || 0; - - //get technicaldetails and metadataQuality - let technicalDetails = await datasetonboardingUtil.buildTechnicalDetails(dataset.structuralMetadata); - let metadataQuality = await datasetonboardingUtil.buildMetadataQuality(dataset, datasetv2Object, dataset.pid); - - // call filterCommercialUsage to determine commericalUse field only pass in v2 a - let commercialUse = filtersService.computeCommericalUse({}, datasetv2Object); - - let updatedDataset = await Data.findOneAndUpdate( - { _id: id }, - { - datasetid: newDatasetVersionId, - datasetVersion: dataset.datasetVersion, - name: dataset.questionAnswers['properties/summary/title'] || '', - description: dataset.questionAnswers['properties/documentation/abstract'] || '', - activeflag: 'active', - tags: { - features: dataset.questionAnswers['properties/summary/keywords'] || [], - }, - commercialUse, - hasTechnicalDetails: !isEmpty(technicalDetails) ? true : false, - 'timestamps.updated': Date.now(), - 'timestamps.published': Date.now(), - counter: previousCounter, - datasetfields: { - publisher: `${publisherData[0].publisherDetails.memberOf} > ${publisherData[0].publisherDetails.name}`, - geographicCoverage: dataset.questionAnswers['properties/coverage/spatial'] || [], - physicalSampleAvailability: dataset.questionAnswers['properties/coverage/physicalSampleAvailability'] || [], - abstract: dataset.questionAnswers['properties/summary/abstract'] || '', - releaseDate: dataset.questionAnswers['properties/provenance/temporal/distributionReleaseDate'] || '', - accessRequestDuration: dataset.questionAnswers['properties/accessibility/access/deliveryLeadTime'] || '', - //conformsTo: dataset.questionAnswers['properties/accessibility/formatAndStandards/conformsTo'] || '', - //accessRights: dataset.questionAnswers['properties/accessibility/access/accessRights'] || '', - //jurisdiction: dataset.questionAnswers['properties/accessibility/access/jurisdiction'] || '', - datasetStartDate: dataset.questionAnswers['properties/provenance/temporal/startDate'] || '', - datasetEndDate: dataset.questionAnswers['properties/provenance/temporal/endDate'] || '', - //statisticalPopulation: datasetMDC.statisticalPopulation, - ageBand: dataset.questionAnswers['properties/coverage/typicalAgeRange'] || '', - contactPoint: dataset.questionAnswers['properties/summary/contactPoint'] || '', - periodicity: dataset.questionAnswers['properties/provenance/temporal/accrualPeriodicity'] || '', - - metadataquality: metadataQuality, - //datautility: dataUtility ? dataUtility : {}, - //metadataschema: metadataSchema && metadataSchema.data ? metadataSchema.data : {}, - technicaldetails: technicalDetails, - //versionLinks: versionLinks && versionLinks.data && versionLinks.data.items ? versionLinks.data.items : [], - phenotypes: [], - }, - datasetv2: datasetv2Object, - applicationStatusDesc: applicationStatusDesc, - discourseTopicId: previousDiscourseTopicId, - }, - { new: true } - ); - - filtersService.optimiseFilters('dataset'); - - let datasetv2DifferenceObject = datasetonboardingUtil.datasetv2ObjectComparison(datasetv2Object, dataset.datasetv2); - - if (!_.isEmpty(datasetv2DifferenceObject)) { - await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_UPDATES_SUBMITTED, { - type: constants.activityLogTypes.DATASET, - updatedDataset, - user: req.user, - differences: datasetv2DifferenceObject, - }); - } - - //emails / notifications - await datasetonboardingUtil.createNotifications(constants.notificationTypes.DATASETAPPROVED, updatedDataset); - - await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_VERSION_APPROVED, { - type: constants.activityLogTypes.DATASET, - updatedDataset, - user: req.user, - }); - }) - .catch(err => { - console.error('Error when trying to create new dataset on the MDC - ' + err.message); - }); - }) - .catch(err => { - console.error('Error when trying to login to MDC - ' + err.message); - }); + dataset = await Data.findOne({ _id: id }); + + if (!dataset) return res.status(404).json({ status: 'error', message: 'Dataset could not be found.' }); + + dataset.questionAnswers = JSON.parse(dataset.questionAnswers); + const publisherData = await PublisherModel.find({ _id: dataset.datasetv2.summary.publisher.identifier }).lean(); + + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); + const [cookie] = responseLogin.headers["set-cookie"]; + _httpClient.setHttpClientCookies(cookie); + + let jsonData = JSON.stringify(await datasetonboardingUtil.buildJSONFile(dataset)); + fs.writeFileSync(__dirname + `/datasetfiles/${dataset._id}.json`, jsonData); + + var data = new FormData(); + data.append('folderId', publisherData[0].mdcFolderId); + data.append('importFile', fs.createReadStream(__dirname + `/datasetfiles/${dataset._id}.json`)); + data.append('finalised', 'false'); + data.append('importAsNewDocumentationVersion', 'true'); + + const responseImport = await _httpClient.post( + metadataCatalogueLink + '/api/dataModels/import/ox.softeng.metadatacatalogue.core.spi.json/JsonImporterService/1.1', + data, + { + withCredentials: true, + timeout: 60000, + headers: { + ...data.getHeaders(), + }, + }); + + let newDatasetVersionId = responseImport.data.items[0].id; + fs.unlinkSync(__dirname + `/datasetfiles/${dataset._id}.json`); - 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); - }); + const updatedDatasetDetails = { + documentationVersion: dataset.datasetVersion, + }; - return res.status(200).json({ status: 'success' }); - } else if (applicationStatus === 'rejected') { - if (userType !== constants.userTypes.ADMIN) { - return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); - } + await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}`, updatedDatasetDetails, { withCredentials: true, timeout: 20000 }); + await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}/finalise`, null, { withCredentials: true, timeout: 20000 }); + + // Adding to DB + let datasetv2Object = await datasetonboardingUtil.buildv2Object(dataset, newDatasetVersionId); + + let previousDataset = await Data.findOneAndUpdate({ pid: dataset.pid, activeflag: 'active' }, { activeflag: 'archive' }); + let previousCounter = 0; + let previousDiscourseTopicId = 0; + if (previousDataset) previousCounter = previousDataset.counter || 0; + if (previousDataset) previousDiscourseTopicId = previousDataset.discourseTopicId || 0; + + //get technicaldetails and metadataQuality + let technicalDetails = await datasetonboardingUtil.buildTechnicalDetails(dataset.structuralMetadata); + let metadataQuality = await datasetonboardingUtil.buildMetadataQuality(dataset, datasetv2Object, dataset.pid); + + // call filterCommercialUsage to determine commericalUse field only pass in v2 a + let commercialUse = filtersService.computeCommericalUse({}, datasetv2Object); + + updatedDataset = await Data.findOneAndUpdate( + { _id: id }, + { + datasetid: newDatasetVersionId, + datasetVersion: dataset.datasetVersion, + name: dataset.questionAnswers['properties/summary/title'] || '', + description: dataset.questionAnswers['properties/documentation/abstract'] || '', + activeflag: 'active', + tags: { + features: dataset.questionAnswers['properties/summary/keywords'] || [], + }, + commercialUse, + hasTechnicalDetails: !isEmpty(technicalDetails) ? true : false, + 'timestamps.updated': Date.now(), + 'timestamps.published': Date.now(), + counter: previousCounter, + datasetfields: { + publisher: `${publisherData[0].publisherDetails.memberOf} > ${publisherData[0].publisherDetails.name}`, + geographicCoverage: dataset.questionAnswers['properties/coverage/spatial'] || [], + physicalSampleAvailability: dataset.questionAnswers['properties/coverage/physicalSampleAvailability'] || [], + abstract: dataset.questionAnswers['properties/summary/abstract'] || '', + releaseDate: dataset.questionAnswers['properties/provenance/temporal/distributionReleaseDate'] || '', + accessRequestDuration: dataset.questionAnswers['properties/accessibility/access/deliveryLeadTime'] || '', + datasetStartDate: dataset.questionAnswers['properties/provenance/temporal/startDate'] || '', + datasetEndDate: dataset.questionAnswers['properties/provenance/temporal/endDate'] || '', + ageBand: dataset.questionAnswers['properties/coverage/typicalAgeRange'] || '', + contactPoint: dataset.questionAnswers['properties/summary/contactPoint'] || '', + periodicity: dataset.questionAnswers['properties/provenance/temporal/accrualPeriodicity'] || '', + metadataquality: metadataQuality, + technicaldetails: technicalDetails, + phenotypes: [], + }, + datasetv2: datasetv2Object, + applicationStatusDesc: applicationStatusDesc, + discourseTopicId: previousDiscourseTopicId, + }, + { new: true } + ); + + filtersService.optimiseFilters('dataset'); + + let datasetv2DifferenceObject = datasetonboardingUtil.datasetv2ObjectComparison(datasetv2Object, dataset.datasetv2); + + if (!_.isEmpty(datasetv2DifferenceObject)) { + await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_UPDATES_SUBMITTED, { + type: constants.activityLogTypes.DATASET, + updatedDataset, + user: req.user, + differences: datasetv2DifferenceObject, + }); + } - let updatedDataset = await Data.findOneAndUpdate( - { _id: id }, - { - activeflag: constants.datasetStatuses.REJECTED, - applicationStatusDesc: applicationStatusDesc, - applicationStatusAuthor: `${firstname} ${lastname}`, - 'timestamps.rejected': Date.now(), - 'timestamps.updated': Date.now(), - }, - { new: true } - ); + //emails / notifications + await datasetonboardingUtil.createNotifications(constants.notificationTypes.DATASETAPPROVED, updatedDataset); - //emails / notifications - await datasetonboardingUtil.createNotifications(constants.notificationTypes.DATASETREJECTED, updatedDataset); + activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_APPROVED; - await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_VERSION_REJECTED, { - type: constants.activityLogTypes.DATASET, - updatedDataset, - user: req.user, - }); + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); - return res.status(200).json({ status: 'success' }); - } else if (applicationStatus === 'archive') { - let dataset = await Data.findOne({ _id: id }).lean(); + break; + case 'rejected': + if (userType !== constants.userTypes.ADMIN) { + return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); + } - if (dataset.timestamps.submitted) { - //soft delete from MDC - let metadataCatalogueLink = process.env.MDC_Config_HDRUK_metadataUrl || 'https://modelcatalogue.cs.ox.ac.uk/hdruk-preprod'; + updatedDataset = await Data.findOneAndUpdate( + { _id: id }, + { + activeflag: constants.datatsetStatuses.REJECTED, + applicationStatusDesc: applicationStatusDesc, + applicationStatusAuthor: `${firstname} ${lastname}`, + 'timestamps.rejected': Date.now(), + 'timestamps.updated': Date.now(), + }, + { new: true } + ); + + //emails / notifications + await datasetonboardingUtil.createNotifications(constants.notificationTypes.DATASETREJECTED, updatedDataset); + + activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_REJECTED; + + break; + case 'archive': + dataset = await Data.findOne({ _id: id }).lean(); + + if (dataset.timestamps.submitted) { + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); + + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); + const [cookie] = responseLogin.headers["set-cookie"]; + _httpClient.setHttpClientCookies(cookie); + + await _httpClient.delete(metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, loginDetails, { withCredentials: true, timeout: 5000 }); + + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); + } + updatedDataset = await Data.findOneAndUpdate( + { _id: id }, + { activeflag: constants.datatsetStatuses.ARCHIVE, 'timestamps.updated': Date.now(), 'timestamps.archived': Date.now() } + ); + + activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_ARCHIVED; + + break; + case 'unarchive': + dataset = await Data.findOne({ _id: id }).lean(); + let flagIs = 'draft'; + if (dataset.timestamps.submitted) { + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); + + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); + const [cookie] = responseLogin.headers["set-cookie"]; + _httpClient.setHttpClientCookies(cookie); + + const updatedDatasetDetails = { + deleted: 'false', + }; - 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); - }); - const loginDetails = { - username: process.env.MDC_Config_HDRUK_username || '', - password: process.env.MDC_Config_HDRUK_password || '', - }; + await _httpClient.put(metadataCatalogueLink + metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, updatedDatasetDetails, { withCredentials: true, timeout: 5000 }); - await axios - .post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { - withCredentials: true, - timeout: 5000, - }) - .then(async session => { - axios.defaults.headers.Cookie = session.headers['set-cookie'][0]; // get cookie from request - - await axios - .delete(metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, { withCredentials: true, timeout: 5000 }) - .catch(err => { - console.error('Error when trying to delete(archive) a dataset - ' + err.message); - }); - }) - .catch(err => { - console.error('Error when trying to login to MDC - ' + err.message); - }); + await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); - 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); - }); - } - let updatedDataset = await Data.findOneAndUpdate( - { _id: id }, - { activeflag: constants.datasetStatuses.ARCHIVE, 'timestamps.updated': Date.now(), 'timestamps.archived': Date.now() } - ); - - await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_VERSION_ARCHIVED, { - type: constants.activityLogTypes.DATASET, - updatedDataset, - user: req.user, - }); - - return res.status(200).json({ status: 'success' }); - } else if (applicationStatus === 'unarchive') { - let dataset = await Data.findOne({ _id: id }).lean(); - let flagIs = 'draft'; - if (dataset.timestamps.submitted) { - let metadataCatalogueLink = process.env.MDC_Config_HDRUK_metadataUrl || 'https://modelcatalogue.cs.ox.ac.uk/hdruk-preprod'; - - 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); - }); - const loginDetails = { - username: process.env.MDC_Config_HDRUK_username || '', - password: process.env.MDC_Config_HDRUK_password || '', - }; + flagIs = 'active'; + } + updatedDataset = await Data.findOneAndUpdate({ _id: id }, { activeflag: flagIs }); //active or draft - await axios - .post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { - withCredentials: true, - timeout: 5000, - }) - .then(async session => { - axios.defaults.headers.Cookie = session.headers['set-cookie'][0]; // get cookie from request - - const updatedDatasetDetails = { - deleted: 'false', - }; - await axios - .put(metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, updatedDatasetDetails, { - withCredentials: true, - timeout: 5000, - }) - .catch(err => { - console.error('Error when trying to update the version number on the MDC - ' + err.message); - }); - }) - .catch(err => { - console.error('Error when trying to login to MDC - ' + err.message); - }); + activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_UNARCHIVED; - 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); + break; + default: + res.status(500).json({ + status: 'error', + message: 'An error occurred - application status is not set correctly', }); - - flagIs = 'active'; - } - const updatedDataset = await Data.findOneAndUpdate({ _id: id }, { activeflag: flagIs }); //active or draft - - await activityLogService.logActivity(constants.activityLogEvents.dataset.DATASET_VERSION_UNARCHIVED, { - type: constants.activityLogTypes.DATASET, - updatedDataset, - user: req.user, - }); - - return res.status(200).json({ status: 'success' }); } + + await activityLogService.logActivity(activityLogStatus, { + type: constants.activityLogTypes.DATASET, + updatedDataset, + user: req.user, + }); + + return res.status(200).json({ status: 'success' }); + } catch (err) { - process.stdout.write(`${err.message}\n`); res.status(500).json({ status: 'error', message: 'An error occurred updating the dataset status', diff --git a/src/services/httpClient/httpClient.js b/src/services/httpClient/httpClient.js new file mode 100644 index 00000000..c18ebb0d --- /dev/null +++ b/src/services/httpClient/httpClient.js @@ -0,0 +1,73 @@ +const axios = require('axios'); + +class HttpClient { + constructor() { + this._axios = axios; + } + + setHttpClientCookies(cookies) { + return axios.defaults.headers.Cookie = cookies; + } + + async post(url, body, options) { + const headers = { + ...(options && options.headers), + Accept: 'application/json', + 'Content-Type': 'application/json;charset=UTF-8', + }; + + try { + const response = await this._axios.post(url, body, { + ...options, + headers, + }); + + return response; + } catch (err) { + console.error(err); + throw new Error(err.message); + } + } + + async put(url, body, options) { + const headers = { + ...(options && options.headers), + Accept: 'application/json', + 'Content-Type': 'application/json;charset=UTF-8', + }; + + try { + const response = await this._axios.put(url, body, { + ...options, + headers, + }); + + return response; + } catch (err) { + console.error(err); + throw new Error(err.message); + } + } + + async delete(url, options) { + const headers = { + ...(options && options.headers), + Accept: 'application/json', + 'Content-Type': 'application/json;charset=UTF-8', + }; + + try { + const response = await this._axios.delete(url, { + ...options, + headers, + }); + + return response; + } catch (err) { + console.error(err); + throw new Error(err.message); + } + } +} + +module.exports = HttpClient; \ No newline at end of file From bf5885c79736e76842033bad036c9725ae9a14c8 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 3 Mar 2022 11:13:08 +0000 Subject: [PATCH 028/389] update nodemon --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6181a578..ff15fcf1 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ }, "scripts": { "start": "node index.js", - "server": "nodemon index.js", + "server": "nodemon --ignore 'src/**/*.json' index.js", "debug": "nodemon --inspect=0.0.0.0:3001 index.js", "build": "", "test": "jest --runInBand", From 97f36be5dee14e4e2d2ac7e843a80a95be556b87 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 4 Mar 2022 13:13:19 +0000 Subject: [PATCH 029/389] orderd by updateAt desc --- src/resources/user/user.service.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index dde0eed9..bce534ed 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -88,22 +88,22 @@ export async function getUsersCollaborators(currentUserId) { let collaborators = []; // Get all collaborators from collections - let collaboratorsCollections = await Collections.find({ authors: currentUserId }, { _id: 0, authors: 1 }); + let collaboratorsCollections = await Collections.find({ authors: currentUserId }, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsCollections, 'authors', collaborators, currentUserId); // Get all collaborators from cohorts - let collaboratorsCohorts = await Cohort.find({ uploaders: currentUserId }, { _id: 0, uploaders: 1 }); + let collaboratorsCohorts = await Cohort.find({ uploaders: currentUserId }, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsCohorts, 'uploaders', collaborators, currentUserId); // Get all collaborators from tools and papers (data collection) - let collaboratorsTools = await Data.find({ authors: currentUserId }, { _id: 0, authors: 1 }); + let collaboratorsTools = await Data.find({ authors: currentUserId }, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsTools, 'authors', collaborators, currentUserId); // Get all collaborators from DARs let collaboratorsDARs = await DataRequestModel.find( { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }, { _id: 0, authorIds: 1, userId: 1 } - ); + ).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsDARs, 'authorIds', collaborators, currentUserId); // Strip out duplicate collaborators, add a count @@ -182,6 +182,7 @@ export async function getUsers(currentUserId) { email: '$user.email', }, }, + { $sort: { updateAt: -1 } } ]); return new Promise((resolve, reject) => { From 026b37f299d62688d3ae91baa38f3040dad1f025 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 4 Mar 2022 13:14:54 +0000 Subject: [PATCH 030/389] update --- src/resources/user/user.service.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index bce534ed..dfed1522 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -182,7 +182,11 @@ export async function getUsers(currentUserId) { email: '$user.email', }, }, - { $sort: { updateAt: -1 } } + { + $sort: { + updateAt: -1 + }, + }, ]); return new Promise((resolve, reject) => { From c2cb49d6287f9c0af0a9bb4c9a6695cb9be9f767 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 7 Mar 2022 12:16:25 +0000 Subject: [PATCH 031/389] create new endpoint --- src/resources/user/user.route.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 83992502..0ef3a21d 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -27,7 +27,22 @@ router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async // @desc get all // @access Private router.get('/', passport.authenticate('jwt'), async (req, res) => { - await getUsers(req.user.id) + let userId = req.user.id; + await getUsers(userId) + .then(response => { + return res.json({ success: true, data: response }); + }) + .catch(err => { + return new Error({ success: false, error: err }); + }); +}); + +// @router GET /api/v1/users/search/:filter +// @desc get all filtered by text +// @access Private +router.get('/search/:filter', passport.authenticate('jwt'), async (req, res) => { + let userId = null; + await getUsers(userId) .then(response => { return res.json({ success: true, data: response }); }) From a3ce13d0ff353112fdef7e503f12a4d6f1792f9a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 7 Mar 2022 12:17:13 +0000 Subject: [PATCH 032/389] update check user by id --- src/resources/user/user.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 83992502..a6573894 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -13,7 +13,7 @@ const router = express.Router(); // @router GET /api/v1/users/:userID // @desc find user by id // @access Private -router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async (req, res) => { +router.get('/:userID', passport.authenticate('jwt'), async (req, res) => { //req.params.id is how you get the id from the url var q = UserModel.find({ id: req.params.userID }); From da75ebcad44e60b8ba0c36c8d88bf28e856ea9bd Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 7 Mar 2022 13:37:31 +0000 Subject: [PATCH 033/389] update --- src/resources/user/user.route.js | 3 ++- src/resources/user/user.service.js | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 0ef3a21d..e5b536a0 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -40,10 +40,11 @@ router.get('/', passport.authenticate('jwt'), async (req, res) => { // @router GET /api/v1/users/search/:filter // @desc get all filtered by text // @access Private -router.get('/search/:filter', passport.authenticate('jwt'), async (req, res) => { +router.get('/search/:filter', async (req, res) => { let userId = null; await getUsers(userId) .then(response => { + console.log(`response: ${JSON.stringify(response)}`); return res.json({ success: true, data: response }); }) .catch(err => { diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index dfed1522..5288ce07 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -86,22 +86,31 @@ export async function setCohortDiscoveryAccess(id, roles) { // Gets all of the logged in users collaborators export async function getUsersCollaborators(currentUserId) { let collaborators = []; + let filter = {}; + + if (currentUserId) { + filter = { authors: currentUserId }; + } // Get all collaborators from collections - let collaboratorsCollections = await Collections.find({ authors: currentUserId }, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); + let collaboratorsCollections = await Collections.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsCollections, 'authors', collaborators, currentUserId); // Get all collaborators from cohorts - let collaboratorsCohorts = await Cohort.find({ uploaders: currentUserId }, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); + let collaboratorsCohorts = await Cohort.find(filter, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsCohorts, 'uploaders', collaborators, currentUserId); // Get all collaborators from tools and papers (data collection) - let collaboratorsTools = await Data.find({ authors: currentUserId }, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); + let collaboratorsTools = await Data.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsTools, 'authors', collaborators, currentUserId); + if (currentUserId) { + filter = { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }; + } + // Get all collaborators from DARs let collaboratorsDARs = await DataRequestModel.find( - { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }, + filter, { _id: 0, authorIds: 1, userId: 1 } ).sort({ updatedAt: -1 }); await populateCollaborators(collaboratorsDARs, 'authorIds', collaborators, currentUserId); @@ -123,12 +132,13 @@ export async function getUsersCollaborators(currentUserId) { async function populateCollaborators(collaboratorsEntity, items, collaborators, currentUserId) { for (const collaborator of collaboratorsEntity) { - if (items === 'authorIds' && collaborator.userId !== currentUserId) { + if ((!currentUserId && items === 'authorIds') + || (currentUserId && items === 'authorIds' && collaborator.userId !== currentUserId)) { collaborators.push(collaborator.userId); } for (const item of collaborator[items]) { - if (item !== currentUserId) { + if (!currentUserId || (currentUserId && item !== currentUserId)) { collaborators.push(item); } } From 0425698e28e904c8bc89e9592f84ead0601b8cce Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 7 Mar 2022 16:17:06 +0000 Subject: [PATCH 034/389] added search user by filter --- src/resources/user/user.route.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index e5b536a0..3b89fbc8 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -7,6 +7,10 @@ import { ROLES } from './user.roles'; import { setCohortDiscoveryAccess, getUsers } from './user.service'; import { upperCase } from 'lodash'; //import { createServiceAccount } from './user.repository'; +import { + checkInputMiddleware, + checkMinLengthMiddleware, +} from '../../middlewares/index'; const router = express.Router(); @@ -40,12 +44,20 @@ router.get('/', passport.authenticate('jwt'), async (req, res) => { // @router GET /api/v1/users/search/:filter // @desc get all filtered by text // @access Private -router.get('/search/:filter', async (req, res) => { +router.get('/search/:filter', [checkInputMiddleware, checkMinLengthMiddleware], async (req, res) => { let userId = null; + let filterString = req.params.filter; await getUsers(userId) .then(response => { - console.log(`response: ${JSON.stringify(response)}`); - return res.json({ success: true, data: response }); + + const usersFiltered = []; + response.map((item) => { + if (item.name.toLowerCase().includes(filterString.toLowerCase())) { + usersFiltered.push(item); + } + }); + + return res.json({ success: true, data: usersFiltered }); }) .catch(err => { return new Error({ success: false, error: err }); From d5428989407b73b6aa897eab288e15a6c11c2f0c Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 8 Mar 2022 09:40:03 +0000 Subject: [PATCH 035/389] update route search and filter --- src/resources/user/user.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 3b89fbc8..24af4f70 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -44,7 +44,7 @@ router.get('/', passport.authenticate('jwt'), async (req, res) => { // @router GET /api/v1/users/search/:filter // @desc get all filtered by text // @access Private -router.get('/search/:filter', [checkInputMiddleware, checkMinLengthMiddleware], async (req, res) => { +router.get('/search/:filter', passport.authenticate('jwt'), [checkInputMiddleware, checkMinLengthMiddleware], async (req, res) => { let userId = null; let filterString = req.params.filter; await getUsers(userId) From 8ac589ab996c6374269757905ab2f22166b382f2 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 8 Mar 2022 09:51:17 +0000 Subject: [PATCH 036/389] update --- src/resources/user/user.route.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 24af4f70..09fa4879 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -31,8 +31,7 @@ router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async // @desc get all // @access Private router.get('/', passport.authenticate('jwt'), async (req, res) => { - let userId = req.user.id; - await getUsers(userId) + await getUsers(req.user.id) .then(response => { return res.json({ success: true, data: response }); }) @@ -45,9 +44,8 @@ router.get('/', passport.authenticate('jwt'), async (req, res) => { // @desc get all filtered by text // @access Private router.get('/search/:filter', passport.authenticate('jwt'), [checkInputMiddleware, checkMinLengthMiddleware], async (req, res) => { - let userId = null; let filterString = req.params.filter; - await getUsers(userId) + await getUsers(null) .then(response => { const usersFiltered = []; From 7bc7da3ccdc07eeb984954e4802bc1ddfba0bf2b Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 8 Mar 2022 09:58:01 +0000 Subject: [PATCH 037/389] update --- src/resources/user/user.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 09fa4879..0bed1929 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -6,7 +6,7 @@ import { UserModel } from './user.model'; import { ROLES } from './user.roles'; import { setCohortDiscoveryAccess, getUsers } from './user.service'; import { upperCase } from 'lodash'; -//import { createServiceAccount } from './user.repository'; + import { checkInputMiddleware, checkMinLengthMiddleware, From 34e0a80e40c9b364e5e77f994fa55a712cd09870 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Tue, 8 Mar 2022 12:34:34 +0000 Subject: [PATCH 038/389] remove duplicate DUR bug --- src/resources/datarequest/datarequest.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index c123270b..e776dc3e 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -697,7 +697,6 @@ export default class DataRequestController extends Controller { this.dataRequestService.updateVersionStatus(accessRecord, accessRecord.applicationStatus); if (accessRecord.applicationStatus === constants.applicationStatuses.APPROVED) { - await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); const dataUseRegister = await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); await dataUseRegisterController.createNotifications( constants.dataUseRegisterNotifications.DATAUSEAPPROVED, @@ -710,7 +709,6 @@ export default class DataRequestController extends Controller { }); } else if (accessRecord.applicationStatus === constants.applicationStatuses.APPROVEDWITHCONDITIONS) { - await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); const dataUseRegister = await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); await dataUseRegisterController.createNotifications( constants.dataUseRegisterNotifications.DATAUSEAPPROVED, From 8e044642fc668f7b783ba061579491dae9f4bb7a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 14:17:41 +0000 Subject: [PATCH 039/389] tests --- src/resources/user/__mocks__/cohorts.data.js | 121 +++++++++++++++ .../user/__mocks__/collections.data.js | 146 ++++++++++++++++++ src/resources/user/__mocks__/tools.data.js | 118 ++++++++++++++ src/resources/user/__mocks__/users.data.js | 50 ++++++ .../__tests__/getCollaboratorsCohorts.test.js | 0 .../getCollaboratorsCollections.test.js | 26 ++++ .../__tests__/getUniqueCollaborators.test.js | 48 ++++++ src/resources/user/user.service.js | 57 +++++-- 8 files changed, 549 insertions(+), 17 deletions(-) create mode 100644 src/resources/user/__mocks__/cohorts.data.js create mode 100644 src/resources/user/__mocks__/collections.data.js create mode 100644 src/resources/user/__mocks__/tools.data.js create mode 100644 src/resources/user/__mocks__/users.data.js create mode 100644 src/resources/user/__tests__/getCollaboratorsCohorts.test.js create mode 100644 src/resources/user/__tests__/getCollaboratorsCollections.test.js create mode 100644 src/resources/user/__tests__/getUniqueCollaborators.test.js diff --git a/src/resources/user/__mocks__/cohorts.data.js b/src/resources/user/__mocks__/cohorts.data.js new file mode 100644 index 00000000..adedc598 --- /dev/null +++ b/src/resources/user/__mocks__/cohorts.data.js @@ -0,0 +1,121 @@ +export const mockCohorts = [ + { + "_id": { + "$oid": "610aabea83eb3f2a4d33ddd1" + }, + "pid": "9a41b63f-5ec5-4966-9d70-f718df24a395", + "description": "a test 1", + "uploaders": [{ + "$numberLong": "5999816745288831" + }, { + "$numberLong": "6936200071297669" + }, { + "$numberLong": "123" + }, { + "$numberLong": "6936200071297669" + }], + "cohort": { + "stuff": "stuff" + }, + "version": 1, + "changeLog": "", + "testArr": ["test1", "test2"], + "id": 1234, + "name": "myCohort", + "updatedAt": { + "$date": "2021-10-07T14:43:55.508Z" + }, + "activeflag": "archived_version", + "type": "cohort", + "relatedObjects": [{ + "_id": { + "$oid": "6141fae77e4d8d8f758e9fb6" + }, + "objectId": "4050303073977839", + "objectType": "project", + "user": "Ciara Ward", + "updated": "21 May 2021" + }, { + "_id": { + "$oid": "6141fb4f7e4d8d8f758e9fb7" + }, + "objectId": "6061998693684476", + "reason": "cohort add via db", + "objectType": "tool", + "user": "Ciara Ward", + "updated": "11 September 2021" + }, { + "_id": { + "$oid": "61431817508c5aa2dce95cdb" + }, + "objectId": "5d76d094-446d-4dcc-baa1-076095f30c23", + "objectType": "dataset", + "pid": "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", + "user": "Ciara Ward", + "updated": "16 September 2021", + "isLocked": true + }, { + "_id": { + "$oid": "614321de508c5aa2dce95cdc" + }, + "objectId": "c6d6bbd3-74ed-46af-841d-ac5e05f4da41", + "objectType": "dataset", + "pid": "f725187f-7352-482b-a43b-64ebc96e66f2", + "user": "Ciara Ward", + "updated": "16 September 2021", + "isLocked": true + }], + "publicflag": true, + "datasetPids": [] + }, + { + "_id": { + "$oid": "610aac0683eb3f2a4d33ddd2" + }, + "pid": "abc12a3", + "description": "a test 2", + "uploaders": [5999816745288800, 947228017269610], + "cohort": { + "stuff": "4444" + }, + "version": 1, + "changeLog": "", + "id": 3456, + "name": "test2 - richard", + "updatedAt": { + "$date": "2021-10-20T13:23:09.093Z" + }, + "activeflag": "active", + "type": "cohort", + "publicflag": false, + "relatedObjects": [{ + "_id": { + "$oid": "614dcb0e1b5e0aa5019aee12" + }, + "objectId": "5d76d094-446d-4dcc-baa1-076095f30c23", + "objectType": "dataset", + "pid": "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", + "user": "Ciara Ward", + "updated": "6 September 2021", + "isLocked": true + }, { + "_id": { + "$oid": "6155ad4116113e65c26a8a4c" + }, + "objectId": "4050303073977839", + "objectType": "project", + "user": "Ciara Ward", + "updated": "28 September 2021" + }, { + "_id": { + "$oid": "6155ada116113e65c26a8a4d" + }, + "reason": "cohort add via db", + "objectType": "tool", + "user": "Ciara Ward", + "updated": "29 September 2021", + "objectId": "6061998693684476" + }], + "datasetPids": [] + } +]; \ No newline at end of file diff --git a/src/resources/user/__mocks__/collections.data.js b/src/resources/user/__mocks__/collections.data.js new file mode 100644 index 00000000..6fa0eb00 --- /dev/null +++ b/src/resources/user/__mocks__/collections.data.js @@ -0,0 +1,146 @@ +export const mockCollections = [ + { + _id: '612e0d035671f75be2461dfa', + authors: [8470291714590256,8470291714590257], + keywords: [], + relatedObjects: [ + { + _id: '612e0d035671f75be2461dfb', + objectId: '6ec3a47b-447a-4b22-9b7a-43acae5d408f', + reason: '', + objectType: 'dataset', + pid: 'fce78329-0de1-45f2-9ff1-e1b4af50528e', + user: 'John Doe', + updated: '31 Aug 2021', + }, + ], + id: 20905331408744290, + name: 'Test', + description: 'TestTestTestTestTestTestTestTestTestTest', + imageLink: '', + activeflag: 'active', + publicflag: true, + updatedon: '2021-08-31T11:06:19.329Z', + createdAt: '2021-08-31T11:05:39.129Z', + updatedAt: '2021-10-14T14:38:21.800Z', + __v: 0, + counter: 3, + persons: [ + { + _id: '6128a6f9dd361d15499db644', + categories: { programmingLanguage: [] }, + tags: { features: [], topics: [] }, + document_links: { doi: [], pdf: [], html: [] }, + datasetfields: { geographicCoverage: [], physicalSampleAvailability: [], technicaldetails: [], versionLinks: [], phenotypes: [] }, + authors: [], + emailNotifications: true, + showOrganisation: true, + structuralMetadata: [], + datasetVersionIsV1: false, + toolids: [], + datasetids: [], + id: 8470291714590257, + type: 'person', + firstname: 'John', + lastname: 'Doe', + bio: '', + link: '', + orcid: 'https://orcid.org/', + activeflag: 'active', + terms: true, + sector: 'Academia', + organisation: '', + showSector: true, + showBio: true, + showLink: true, + showOrcid: true, + showDomain: true, + profileComplete: true, + relatedObjects: [], + programmingLanguage: [], + createdAt: '2021-08-27T08:48:57.710Z', + updatedAt: '2021-08-27T10:23:11.582Z', + __v: 0, + counter: 1, + }, + ], + }, +]; + + +// export const mockCollections = [ +// { +// _id: { +// oid: '6168030b0e24c03595166261', +// }, +// authors: [12345], +// keywords: [], +// relatedObjects: [ +// { +// _id: { +// $oid: '6168030b0e24c03595166262', +// }, +// objectId: 'af434b05-52a7-4ff1-92f5-e2dd38a574aa', +// reason: '', +// objectType: 'dataset', +// pid: 'fdd9e5ab-442f-45d0-a004-f581a3ac809c', +// user: 'John Doe', +// updated: '14 Oct 2021', +// }, +// ], +// id: 138879762298581, +// name: 'Test collection 1', +// description: 'A test collection', +// imageLink: '', +// activeflag: 'active', +// publicflag: true, +// updatedon: { +// $date: '2021-10-14T12:10:13.817Z', +// }, +// createdAt: { +// $date: '2021-10-14T10:14:35.308Z', +// }, +// updatedAt: { +// $date: '2021-10-14T12:10:14.563Z', +// }, +// __v: 0, +// counter: 1, +// }, +// { +// _id: { +// oid: '6168030b0e24c03595166262', +// }, +// authors: [12345], +// keywords: [], +// relatedObjects: [ +// { +// _id: { +// $oid: '6168030b0e24c03595166262', +// }, +// objectId: 'af434b05-52a7-4ff1-92f5-e2dd38a574aa', +// reason: '', +// objectType: 'dataset', +// pid: 'fdd9e5ab-442f-45d0-a004-f581a3ac809c', +// user: 'John Doe', +// updated: '14 Oct 2021', +// }, +// ], +// id: 138879762298582, +// name: 'Test collection 2', +// description: 'A test collection', +// imageLink: '', +// activeflag: 'active', +// publicflag: true, +// updatedon: { +// $date: '2021-10-14T12:10:13.817Z', +// }, +// createdAt: { +// $date: '2021-10-14T10:14:35.308Z', +// }, +// updatedAt: { +// $date: '2021-10-14T12:10:14.563Z', +// }, +// __v: 0, +// counter: 1, +// }, +// ]; diff --git a/src/resources/user/__mocks__/tools.data.js b/src/resources/user/__mocks__/tools.data.js new file mode 100644 index 00000000..d46afb1b --- /dev/null +++ b/src/resources/user/__mocks__/tools.data.js @@ -0,0 +1,118 @@ +export const mockTools = [ + { + "_id": { + "$oid": "618a6dfef53d81176faa3828" + }, + "categories": { + "programmingLanguage": [] + }, + "tags": { + "features": [], + "topics": [] + }, + "document_links": { + "doi": [], + "pdf": [], + "html": [] + }, + "datasetfields": { + "geographicCoverage": [], + "physicalSampleAvailability": [], + "technicaldetails": [], + "versionLinks": [], + "phenotypes": [] + }, + "authors": [], + "emailNotifications": true, + "showOrganisation": true, + "structuralMetadata": [], + "datasetVersionIsV1": false, + "isCohortDiscovery": false, + "toolids": [], + "datasetids": [], + "id": 9285155117905740, + "type": "person", + "firstname": "Joseph", + "lastname": "Best", + "bio": "", + "link": "", + "orcid": "https://orcid.org/", + "activeflag": "active", + "terms": true, + "sector": "Charity/Non-profit", + "organisation": "", + "showSector": true, + "showBio": true, + "showLink": true, + "showOrcid": true, + "showDomain": true, + "profileComplete": true, + "relatedObjects": [], + "programmingLanguage": [], + "createdAt": { + "$date": "2021-11-09T12:47:58.360Z" + }, + "updatedAt": { + "$date": "2021-11-09T12:47:58.360Z" + }, + "__v": 0 + }, + { + "_id": { + "$oid": "619390f4619256bde461d2a3" + }, + "categories": { + "programmingLanguage": [] + }, + "tags": { + "features": [], + "topics": [] + }, + "document_links": { + "doi": [], + "pdf": [], + "html": [] + }, + "datasetfields": { + "geographicCoverage": [], + "physicalSampleAvailability": [], + "technicaldetails": [], + "versionLinks": [], + "phenotypes": [] + }, + "authors": [], + "emailNotifications": true, + "showOrganisation": true, + "structuralMetadata": [], + "datasetVersionIsV1": false, + "isCohortDiscovery": false, + "toolids": [], + "datasetids": [], + "id": 9248830212478532, + "type": "person", + "firstname": "HDRUK", + "lastname": "Developer", + "bio": "", + "link": "", + "orcid": "https://orcid.org/0000-0002-7770-8811", + "activeflag": "active", + "terms": true, + "sector": "Charity/Non-profit", + "organisation": "", + "showSector": true, + "showBio": true, + "showLink": true, + "showOrcid": true, + "showDomain": true, + "profileComplete": true, + "relatedObjects": [], + "programmingLanguage": [], + "createdAt": { + "$date": "2021-11-16T11:07:32.009Z" + }, + "updatedAt": { + "$date": "2021-11-16T11:07:32.009Z" + }, + "__v": 0 + } +]; \ No newline at end of file diff --git a/src/resources/user/__mocks__/users.data.js b/src/resources/user/__mocks__/users.data.js new file mode 100644 index 00000000..94875524 --- /dev/null +++ b/src/resources/user/__mocks__/users.data.js @@ -0,0 +1,50 @@ +export const mockUsers = [ + { + _id: { + oid: '5e6f984a0a7300dc8f6fb195', + }, + id: 12345, + providerId: '102167422686846649659', + provider: 'google', + firstname: 'FirstName1', + lastname: 'LastName1', + email: 'FirstName1.LastName1@test.com', + password: null, + role: 'Admin', + __v: 0, + updatedAt: { + $date: '2021-10-14T12:10:14.563Z', + }, + discourseKey: 'e2ca8f3f13da82a108db03079ae301b7bab59beb56b8', + discourseUsername: 'FirstName1.LastName1', + createdAt: { + $date: '2020-09-04T00:00:00.000Z', + }, + feedback: false, + news: false + }, + { + _id: { + oid: '5e6f984a0a7300dc8f6fb196', + }, + id: 12346, + providerId: '102167422686846649659', + provider: 'google', + firstname: 'FirstName2', + lastname: 'LastName2', + email: 'FirstName2.LastName2@test.com', + password: null, + role: 'Admin', + __v: 0, + updatedAt: { + $date: '2021-10-14T12:10:14.563Z', + }, + discourseKey: 'e2ca8f3f13da82a108db03079ae301b7bab59beb56c8', + discourseUsername: 'FirstName2.LastName2', + createdAt: { + $date: '2020-09-04T00:00:00.000Z', + }, + feedback: false, + news: false + }, +]; \ No newline at end of file diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js new file mode 100644 index 00000000..e69de29b diff --git a/src/resources/user/__tests__/getCollaboratorsCollections.test.js b/src/resources/user/__tests__/getCollaboratorsCollections.test.js new file mode 100644 index 00000000..a97bc7ab --- /dev/null +++ b/src/resources/user/__tests__/getCollaboratorsCollections.test.js @@ -0,0 +1,26 @@ +import dbHandler from '../../../config/in-memory-db'; +import {mockCollections} from '../__mocks__/collections.data'; + +const {getCollaboratorsCollections} = require('../user.service'); + + +beforeAll(async () => { + await dbHandler.connect(); + await dbHandler.loadData({ collections: mockCollections }); +}); + +afterAll(async () => { + await dbHandler.clearDatabase(); + await dbHandler.closeDatabase(); +}); + +describe('getUniqueCollaborators should return one value', () => { + it('should return values', async () => { + const currentUserId = 8470291714590257; + const filter = currentUserId ? {} : { authors: currentUserId }; + + const result = await getCollaboratorsCollections(filter, currentUserId); + expect(result.length).toBe(1); + expect(typeof result).toBe('object'); + }); +}); \ No newline at end of file diff --git a/src/resources/user/__tests__/getUniqueCollaborators.test.js b/src/resources/user/__tests__/getUniqueCollaborators.test.js new file mode 100644 index 00000000..0c8ebb40 --- /dev/null +++ b/src/resources/user/__tests__/getUniqueCollaborators.test.js @@ -0,0 +1,48 @@ +const {getUniqueCollaborators} = require('../user.service'); + + +describe('getUniqueCollaborators', () => { + it('should return a unique collaborator like map', () => { + let collaborators = [39025048818527176,917335621870613]; + const result = getUniqueCollaborators(collaborators); + + expect(result instanceof Map).toBe(true); + }); + + it('should return a specific number of unique collaborators', () => { + let collaborators = [39025048818527176,917335621870613]; + const result = getUniqueCollaborators(collaborators); + + expect(result.size).toBe(2); + }); + + it('should return empty', () => { + let collaborators = []; + const result = getUniqueCollaborators(collaborators); + + expect(result.size).toBe(0); + }); + + it('should return values', () => { + let collaborators = [39025048818527176,917335621870613]; + const result = getUniqueCollaborators(collaborators); + + expect(result.has(39025048818527176)).toBe(true); + expect(result.has(917335621870613)).toBe(true); + }) + + + it('should return correct keys and values', () => { + let collaborators = [39025048818527176,917335621870613]; + const result = getUniqueCollaborators(collaborators); + + const mapValues = [...result.values()]; + const typeMapValues = typeof mapValues; + + const mapKeys = [...result.keys()]; + const typeMapKeys = typeof mapKeys; + + expect(typeMapValues).toBe('object'); + expect(typeMapKeys).toBe('object'); + }) +}); \ No newline at end of file diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 5288ce07..a8a8d324 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -6,6 +6,8 @@ import { Cohort } from '../cohort/cohort.model'; import { Collections } from '../collections/collections.model'; import { DataRequestModel } from '../datarequest/datarequest.model'; +const arrCollaborators = []; + export async function createUser({ firstname, lastname, email, providerId, provider, role }) { return new Promise(async resolve => { const id = parseInt(Math.random().toString().replace('0.', '')); @@ -84,8 +86,7 @@ export async function setCohortDiscoveryAccess(id, roles) { } // Gets all of the logged in users collaborators -export async function getUsersCollaborators(currentUserId) { - let collaborators = []; +export const getUsersCollaborators = async (currentUserId) => { let filter = {}; if (currentUserId) { @@ -93,31 +94,50 @@ export async function getUsersCollaborators(currentUserId) { } // Get all collaborators from collections - let collaboratorsCollections = await Collections.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); - await populateCollaborators(collaboratorsCollections, 'authors', collaborators, currentUserId); + await getCollaboratorsCollections(filter, currentUserId); // Get all collaborators from cohorts - let collaboratorsCohorts = await Cohort.find(filter, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); - await populateCollaborators(collaboratorsCohorts, 'uploaders', collaborators, currentUserId); + await getCollaboratorsCohorts(filter, currentUserId); // Get all collaborators from tools and papers (data collection) - let collaboratorsTools = await Data.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); - await populateCollaborators(collaboratorsTools, 'authors', collaborators, currentUserId); + await getCollaboratorsTools(filter, currentUserId); if (currentUserId) { filter = { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }; } // Get all collaborators from DARs + await getCollaboratorsDARs(filter, currentUserId); + + // Strip out duplicate collaborators, add a count + return getUniqueCollaborators(arrCollaborators); +} + +export const getCollaboratorsCollections = async (filter, currentUserId) => { + let collaboratorsCollections = await Collections.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); + return await populateCollaborators(collaboratorsCollections, 'authors', currentUserId); +} + +export const getCollaboratorsCohorts = async (filter, currentUserId) => { + let collaboratorsCohorts = await Cohort.find(filter, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); + return await populateCollaborators(collaboratorsCohorts, 'uploaders', currentUserId); +} + +export const getCollaboratorsTools = async (filter, currentUserId) => { + let collaboratorsTools = await Data.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); + return await populateCollaborators(collaboratorsTools, 'authors', currentUserId); +} + +export const getCollaboratorsDARs = async (filter, currentUserId) => { let collaboratorsDARs = await DataRequestModel.find( filter, { _id: 0, authorIds: 1, userId: 1 } ).sort({ updatedAt: -1 }); - await populateCollaborators(collaboratorsDARs, 'authorIds', collaborators, currentUserId); + return await populateCollaborators(collaboratorsDARs, 'authorIds', currentUserId); +} - // Strip out duplicate collaborators, add a count +export const getUniqueCollaborators = (collaborators) => { let uniqueCollaborators = new Map(); - for (const collaborator of collaborators) { if (uniqueCollaborators.has(collaborator)) { let incrementedValue = uniqueCollaborators.get(collaborator) + 1; @@ -130,24 +150,27 @@ export async function getUsersCollaborators(currentUserId) { return uniqueCollaborators; } -async function populateCollaborators(collaboratorsEntity, items, collaborators, currentUserId) { +export const populateCollaborators = async (collaboratorsEntity, items, currentUserId) => { + console.log(`collaboratorsEntity: ${JSON.stringify(collaboratorsEntity)}`); for (const collaborator of collaboratorsEntity) { if ((!currentUserId && items === 'authorIds') - || (currentUserId && items === 'authorIds' && collaborator.userId !== currentUserId)) { - collaborators.push(collaborator.userId); + || (currentUserId && items === 'authorIds' && arrCollaborators.userId !== currentUserId)) { + arrCollaborators.push(collaborator.userId); } + console.log(`collaborator[items]: ${JSON.stringify(collaborator[items])}`); for (const item of collaborator[items]) { + console.log(item); if (!currentUserId || (currentUserId && item !== currentUserId)) { - collaborators.push(item); + arrCollaborators.push(item); } } } - return collaborators; + return arrCollaborators; } -export async function getUsers(currentUserId) { +export const getUsers = async (currentUserId) => { // Get the users collaborators let usersCollaborators = await getUsersCollaborators(currentUserId); From 38016838c9006fc6d99d674f6207ddfbcdd6ea53 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 14:18:48 +0000 Subject: [PATCH 040/389] rollback for current user --- src/resources/user/user.route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 43e3fbb7..0bed1929 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -17,7 +17,7 @@ const router = express.Router(); // @router GET /api/v1/users/:userID // @desc find user by id // @access Private -router.get('/:userID', passport.authenticate('jwt'), async (req, res) => { +router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async (req, res) => { //req.params.id is how you get the id from the url var q = UserModel.find({ id: req.params.userID }); From e584627667b2a9f977c21f8a33635d6cea6c7888 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 15:55:39 +0000 Subject: [PATCH 041/389] update --- src/resources/user/__mocks__/cohorts.data.js | 176 ++++++++---------- .../__tests__/getCollaboratorsCohorts.test.js | 36 ++++ .../getCollaboratorsCollections.test.js | 2 +- src/resources/user/user.service.js | 8 +- 4 files changed, 114 insertions(+), 108 deletions(-) diff --git a/src/resources/user/__mocks__/cohorts.data.js b/src/resources/user/__mocks__/cohorts.data.js index adedc598..60851d22 100644 --- a/src/resources/user/__mocks__/cohorts.data.js +++ b/src/resources/user/__mocks__/cohorts.data.js @@ -1,120 +1,90 @@ export const mockCohorts = [ { - "_id": { - "$oid": "610aabea83eb3f2a4d33ddd1" + _id: "610aabea83eb3f2a4d33ddd1", + pid: "9a41b63f-5ec5-4966-9d70-f718df24a395", + description: "a test 1", + uploaders: [8470291714590256,8470291714590257], + cohort: { + stuff: "stuff" }, - "pid": "9a41b63f-5ec5-4966-9d70-f718df24a395", - "description": "a test 1", - "uploaders": [{ - "$numberLong": "5999816745288831" + version: 1, + changeLog: "", + testArr: ["test1", "test2"], + id: 1234, + name: "Cohort One", + updatedAt: "2021-10-07T14:43:55.508Z", + activeflag: "archived_version", + type: "cohort", + relatedObjects: [{ + _id: "6141fae77e4d8d8f758e9fb6", + objectId: "4050303073977839", + objectType: "project", + user: "User Name One", + updated: "21 May 2021" }, { - "$numberLong": "6936200071297669" + _id: "6141fb4f7e4d8d8f758e9fb7", + objectId: "6061998693684476", + reason: "cohort add via db", + objectType: "tool", + user: "User Name One", + updated: "11 September 2021" }, { - "$numberLong": "123" + _id: "61431817508c5aa2dce95cdb", + objectId: "5d76d094-446d-4dcc-baa1-076095f30c23", + objectType: "dataset", + pid: "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", + user: "User Name One", + updated: "16 September 2021", + isLocked: true }, { - "$numberLong": "6936200071297669" - }], - "cohort": { - "stuff": "stuff" - }, - "version": 1, - "changeLog": "", - "testArr": ["test1", "test2"], - "id": 1234, - "name": "myCohort", - "updatedAt": { - "$date": "2021-10-07T14:43:55.508Z" - }, - "activeflag": "archived_version", - "type": "cohort", - "relatedObjects": [{ - "_id": { - "$oid": "6141fae77e4d8d8f758e9fb6" - }, - "objectId": "4050303073977839", - "objectType": "project", - "user": "Ciara Ward", - "updated": "21 May 2021" - }, { - "_id": { - "$oid": "6141fb4f7e4d8d8f758e9fb7" - }, - "objectId": "6061998693684476", - "reason": "cohort add via db", - "objectType": "tool", - "user": "Ciara Ward", - "updated": "11 September 2021" - }, { - "_id": { - "$oid": "61431817508c5aa2dce95cdb" - }, - "objectId": "5d76d094-446d-4dcc-baa1-076095f30c23", - "objectType": "dataset", - "pid": "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", - "user": "Ciara Ward", - "updated": "16 September 2021", - "isLocked": true - }, { - "_id": { - "$oid": "614321de508c5aa2dce95cdc" - }, - "objectId": "c6d6bbd3-74ed-46af-841d-ac5e05f4da41", - "objectType": "dataset", - "pid": "f725187f-7352-482b-a43b-64ebc96e66f2", - "user": "Ciara Ward", - "updated": "16 September 2021", - "isLocked": true + _id: "614321de508c5aa2dce95cdc", + objectId: "c6d6bbd3-74ed-46af-841d-ac5e05f4da41", + objectType: "dataset", + pid: "f725187f-7352-482b-a43b-64ebc96e66f2", + user: "User Name One", + updated: "16 September 2021", + isLocked: true }], "publicflag": true, "datasetPids": [] }, { - "_id": { - "$oid": "610aac0683eb3f2a4d33ddd2" - }, - "pid": "abc12a3", - "description": "a test 2", - "uploaders": [5999816745288800, 947228017269610], - "cohort": { - "stuff": "4444" - }, - "version": 1, - "changeLog": "", - "id": 3456, - "name": "test2 - richard", - "updatedAt": { - "$date": "2021-10-20T13:23:09.093Z" + _id: "610aac0683eb3f2a4d33ddd2", + pid: "abc12a3", + description: "a test 2", + uploaders: [8470291714590256,8470291714590257], + cohort: { + stuff: "4444" }, - "activeflag": "active", - "type": "cohort", - "publicflag": false, - "relatedObjects": [{ - "_id": { - "$oid": "614dcb0e1b5e0aa5019aee12" - }, - "objectId": "5d76d094-446d-4dcc-baa1-076095f30c23", - "objectType": "dataset", - "pid": "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", - "user": "Ciara Ward", - "updated": "6 September 2021", - "isLocked": true + version: 1, + changeLog: "", + id: 3456, + name: "Cohort Two", + updatedAt: "2021-10-20T13:23:09.093Z", + activeflag: "active", + type: "cohort", + publicflag: false, + relatedObjects: [{ + _id: "614dcb0e1b5e0aa5019aee12", + objectId: "5d76d094-446d-4dcc-baa1-076095f30c23", + objectType: "dataset", + pid: "0bb8d80b-4d92-4bcb-84b7-5a1ff1f86a33", + user: "User Name One", + updated: "6 September 2021", + isLocked: true }, { - "_id": { - "$oid": "6155ad4116113e65c26a8a4c" - }, - "objectId": "4050303073977839", - "objectType": "project", - "user": "Ciara Ward", - "updated": "28 September 2021" + _id: "6155ad4116113e65c26a8a4c", + objectId: "4050303073977839", + objectType: "project", + user: "User Name One", + updated: "28 September 2021" }, { - "_id": { - "$oid": "6155ada116113e65c26a8a4d" - }, - "reason": "cohort add via db", - "objectType": "tool", - "user": "Ciara Ward", - "updated": "29 September 2021", - "objectId": "6061998693684476" + _id: "6155ada116113e65c26a8a4d", + reason: "cohort add via db", + objectType: "tool", + user: "User Name One", + updated: "29 September 2021", + objectId: "6061998693684476" }], "datasetPids": [] } diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js index e69de29b..8efdb6bd 100644 --- a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js +++ b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js @@ -0,0 +1,36 @@ +import dbHandler from '../../../config/in-memory-db'; +import {mockCohorts} from '../__mocks__/cohorts.data'; + +const {getCollaboratorsCohorts} = require('../user.service'); + + +beforeAll(async () => { + await dbHandler.connect(); + await dbHandler.loadData({ cohorts: mockCohorts }); +}); + +afterAll(async () => { + await dbHandler.clearDatabase(); + await dbHandler.closeDatabase(); +}); + +describe('getCollaboratorsCohorts should return one value', () => { + it('should return values', async () => { + const currentUserId = 8470291714590257; + const filter = currentUserId ? {} : { uploaders: currentUserId }; + + const result = await getCollaboratorsCohorts(filter, currentUserId); + expect(result.length > 0).toBe(true); + expect(typeof result).toBe('object'); + }); + + it('should return values', async () => { + const currentUserId = null; + const filter = currentUserId ? {} : { uploaders: currentUserId }; + + const result = await getCollaboratorsCohorts(filter, currentUserId); + console.log(`result : ${JSON.stringify(result)}`); + // expect(result.length > 0).toBe(true); + expect(typeof result).toBe('object'); + }); +}); \ No newline at end of file diff --git a/src/resources/user/__tests__/getCollaboratorsCollections.test.js b/src/resources/user/__tests__/getCollaboratorsCollections.test.js index a97bc7ab..70458de3 100644 --- a/src/resources/user/__tests__/getCollaboratorsCollections.test.js +++ b/src/resources/user/__tests__/getCollaboratorsCollections.test.js @@ -17,7 +17,7 @@ afterAll(async () => { describe('getUniqueCollaborators should return one value', () => { it('should return values', async () => { const currentUserId = 8470291714590257; - const filter = currentUserId ? {} : { authors: currentUserId }; + const filter = currentUserId ? { authors: currentUserId } : {}; const result = await getCollaboratorsCollections(filter, currentUserId); expect(result.length).toBe(1); diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index a8a8d324..f24b5063 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -87,15 +87,15 @@ export async function setCohortDiscoveryAccess(id, roles) { // Gets all of the logged in users collaborators export const getUsersCollaborators = async (currentUserId) => { - let filter = {}; + let filter = null; - if (currentUserId) { - filter = { authors: currentUserId }; - } + filter = currentUserId ? { authors: currentUserId } : {}; // Get all collaborators from collections await getCollaboratorsCollections(filter, currentUserId); + filter = currentUserId ? {} : { uploaders: currentUserId }; + // Get all collaborators from cohorts await getCollaboratorsCohorts(filter, currentUserId); From 9fd140e3722e15f48a081452bd443ef7694b0619 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 16:02:00 +0000 Subject: [PATCH 042/389] just collaborators --- src/resources/user/user.service.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 5288ce07..b4e7a737 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -234,7 +234,11 @@ export async function getUsers(currentUserId) { return collaborator.user; }); - resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + if (currentUserId) { + resolve([...collaboratorUsers]); + } else { + resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + } }); }); } From 11674d1c7532a4a3585efd2a9931d7b8f39a7cf5 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 17:46:49 +0000 Subject: [PATCH 043/389] new tests --- src/resources/user/__mocks__/dars.data.js | 359 ++++++++++++++++++ src/resources/user/__mocks__/tools.data.js | 196 +++++----- .../__tests__/getCollaboratorsCohorts.test.js | 8 +- .../getCollaboratorsCollections.test.js | 2 +- .../__tests__/getCollaboratorsDARs.test.js | 36 ++ .../__tests__/getCollaboratorsTools.test.js | 35 ++ .../__tests__/getUniqueCollaborators.test.js | 2 +- src/resources/user/user.service.js | 14 +- 8 files changed, 532 insertions(+), 120 deletions(-) create mode 100644 src/resources/user/__mocks__/dars.data.js create mode 100644 src/resources/user/__tests__/getCollaboratorsDARs.test.js create mode 100644 src/resources/user/__tests__/getCollaboratorsTools.test.js diff --git a/src/resources/user/__mocks__/dars.data.js b/src/resources/user/__mocks__/dars.data.js new file mode 100644 index 00000000..a542e8df --- /dev/null +++ b/src/resources/user/__mocks__/dars.data.js @@ -0,0 +1,359 @@ +export const mockDars = [ + { + _id: "61f1143078397b350634dac3", + majorVersion: 1, + authorIds: [8470291714590256,8470291714590257], + datasetIds: ["9c457d37-3402-450a-8bbd-32bf51524ded"], + initialDatasetIds: [], + datasetTitles: ["Demo v4", "Demo v4-duplicate"], + applicationStatus: "inProgress", + applicationType: "initial", + publisher: "ALLIANCE > SAIL", + formType: "5 safe", + isShared: false, + userId: 7789224198062117, + isCloneable: true, + jsonSchema: { + pages: [{ + description: "Who is going to be accessing the data?\n\nSafe People should have the right motivations for accessing research data and understand the legal and ethical considerations when using data that may be sensitive or confidential. Safe People should also have sufficient skills, knowledge and experience to work with the data effectively. Researchers may need to undergo specific training or accreditation before accessing certain data or research environments and demonstrate that they are part of a bona fide research organisation.\n\nThe purpose of this section is to ensure that:\n- details of people who will be accessing the data and the people who are responsible for completing the application are identified\n- any individual or organisation that intends to access the data requested is identified\n- all identified individuals have the necessary accreditation and/or expertise to work with the data effectively.", + pageId: "safepeople", + title: "Safe people", + active: true + }, { + title: "Safe project", + active: false, + pageId: "safeproject", + description: "What is the purpose of accessing the data?\n\nSafe projects are those that have a valid research purpose with a defined public benefit. \nFor access to data to be granted the researchers need to demonstrate that their proposal is an appropriate and ethical use of the data, and that it is intended to deliver clear public benefits. The purpose of this section is to ensure that:\n- the project rationale is explained in lay terms\n- the research purpose has a defined public benefit. This can be new knowledge, new treatments, improved pathways of care, new techniques of training staff. \n- how the data requested will be used to achieve the project objectives is articulated." + }, { + active: false, + title: "Safe data", + description: "Safe data ensure that researchers have a clear legal basis for accessing the data and do not inadvertently learn something about the data subjects during the course of their analysis, minimising the risks of re-identification.\nThe minimisation of this risk could be achieved by removing direct identifiers, aggregating values, banding variables, or other statistical techniques that may make re-identification more difficult. Sensitive or confidential data could not be considered to be completely safe because of the residual risk to a data subject’s confidentiality. Hence other limitations on access will need to be applied.\n\nThe purpose of this section is to ensure that: \n- there is a clear legal basis for accessing the requested data\n- the data requested is proportionate to the requirement of the project \n- all data requested is necessary in order to achieve the public benefit declared \n- data subjects cannot be identified by your team by cross-referencing datasets from anywhere else.", + pageId: "safedata" + }, { + description: "Safe settings are analytics environments where researchers can access and analyse the requested datasets in a safe and ethical way. Safe settings encompass the physical environment and procedural arrangements such as the supervision and auditing regimes. For safe settings, the likelihood of both deliberate and accidental disclosure needs to be explicitly considered.\n\nThe purpose of this section is to ensure that:\n\n- researchers access requested data in a secure and controlled setting such as a Trusted Research Environment (TRE) that limits the unauthorised use of the data\n- practical controls and appropriate restrictions are in place if researchers access data though non-TRE environment. There may be requirements that data is held on restricted access servers, encrypted and only decrypted at the point of use.", + pageId: "safesettings", + title: "Safe settings", + active: false + }, { + pageId: "safeoutputs", + description: "Safe outputs ensure that all research outputs cannot be used to identify data subjects. They typically include ‘descriptive statistics’ that have been sufficiently aggregated such that identification is near enough impossible, and modelled outputs which are inherently non-confidential. The purpose of this section is to ensure that:\n\n- controls are in place to minimise risks associated with planned outputs and publications \n- the researchers aim to openly publish their results to enable use, scrutiny and further research.", + title: "Safe outputs", + active: false + }], + formPanels: [{ + index: 1, + pageId: "safepeople", + panelId: "primaryapplicant" + }, { + panelId: "safepeople-otherindividuals", + pageId: "safepeople", + index: 2 + }, { + panelId: "safeproject-aboutthisapplication", + pageId: "safeproject", + index: 3 + }, { + pageId: "safeproject", + index: 4, + panelId: "safeproject-projectdetails" + }, { + panelId: "safeproject-funderinformation", + pageId: "safeproject", + index: 5 + }, { + index: 6, + pageId: "safeproject", + panelId: "safeproject-sponsorinformation" + }, { + panelId: "safeproject-declarationofinterest", + pageId: "safeproject", + index: 7 + }, { + pageId: "safeproject", + index: 8, + panelId: "safeproject-intellectualproperty" + }, { + index: 9, + pageId: "safedata", + panelId: "safedata-datafields" + }, { + panelId: "safedata-analysis", + index: 10, + pageId: "safedata" + }, { + panelId: "safedata-otherdatasetsintentiontolinkdata", + pageId: "safedata", + index: 11 + }, { + panelId: "safedata-lawfulbasis", + index: 12, + pageId: "safedata" + }, { + panelId: "safedata-confidentialityavenue", + index: 13, + pageId: "safedata" + }, { + pageId: "safedata", + index: 14, + panelId: "safedata-ethicalapproval" + }, { + panelId: "safesettings-storageandprocessing", + pageId: "safesettings", + index: 15 + }, { + pageId: "safesettings", + index: 16, + panelId: "safesettings-dataflow" + }, { + index: 17, + pageId: "safeoutputs", + panelId: "safeoutputs-outputsdisseminationplans" + }, { + panelId: "safeoutputs-retention", + index: 18, + pageId: "safeoutputs" + }, { + index: 19, + pageId: "safeoutputs", + panelId: "safeoutputs-archiving" + }], + questionPanels: [{ + pageId: "safepeople", + panelHeader: "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \r\n\r\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \r\n\r\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\r\n\r\nPlease use the file upload function if you're not able to add all individuals via the form.\r", + questionSets: [{ + questionSetId: "primaryapplicant", + index: 1 + }], + navHeader: "Primary applicant", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "primaryapplicant" + }, { + pageId: "safepeople", + panelHeader: "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \r\n\r\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \r\n\r\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\r\n\r\nPlease use the file upload function if you're not able to add all individuals via the form.\r", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safepeople-otherindividuals", + questionSets: [{ + questionSetId: "safepeople-otherindividuals", + index: 1 + }, { + index: 100, + questionSetId: "add-safepeople-otherindividuals" + }], + navHeader: "Other individuals" + }, { + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safeproject-aboutthisapplication", + questionSets: [{ + index: 1, + questionSetId: "safeproject-aboutthisapplication" + }], + navHeader: "About this application", + panelHeader: "", + pageId: "safeproject" + }, { + panelHeader: "", + pageId: "safeproject", + panelId: "safeproject-projectdetails", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Project details", + questionSets: [{ + questionSetId: "safeproject-projectdetails", + index: 1 + }] + }, { + navHeader: "Funder information", + questionSets: [{ + index: 1, + questionSetId: "safeproject-funderinformation" + }], + panelId: "safeproject-funderinformation", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelHeader: "A funder is the organisation or body providing the financial resource to make the project possible, and may be different to the organisation detailed in the Safe people section. Please provide details of the main funder organisations supporting this project.\r\n\r\nPlease use the file upload function if you're not able to add all funders via the form.\r", + pageId: "safeproject" + }, { + panelId: "safeproject-sponsorinformation", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Sponsor information", + questionSets: [{ + index: 1, + questionSetId: "safeproject-sponsorinformation" + }], + panelHeader: "The sponsor is usually, but does not have to be, the main funder of the research. The sponsor takes primary responsibility for ensuring that the design of the project meets appropriate standards and that arrangements are in place to ensure appropriate conduct and reporting.\r\n\r\nPlease use the file upload function if you're not able to add all sponsors via the form.\r\n", + pageId: "safeproject" + }, { + navHeader: "Declaration of interest", + questionSets: [{ + questionSetId: "safeproject-declarationofinterest", + index: 1 + }], + panelId: "safeproject-declarationofinterest", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelHeader: "All interests that might unduly influence an individual’s judgement and objectivity in the use of the data being requested are of relevance, particularly if it involves payment or financial inducement. \r\n\r\nThese might include any involvement of commercial organisations at arm’s-length to the project, or likely impact on commercial organisations, individually or collectively, that might result from the outcomes or methodology of the project.\r\n\r\nAll individuals named in this application who have an interest this application must declare their interest.\r", + pageId: "safeproject" + }, { + panelHeader: "All interests that might unduly influence an individual’s judgement and objectivity in the use of the data being requested are of relevance, particularly if it involves payment or financial inducement. \r\n\r\nThese might include any involvement of commercial organisations at arm’s-length to the project, or likely impact on commercial organisations, individually or collectively, that might result from the outcomes or methodology of the project.\r\n\r\nAll individuals named in this application who have an interest this application must declare their interest.\r", + pageId: "safeproject", + questionSets: [{ + questionSetId: "safeproject-intellectualproperty", + index: 1 + }], + navHeader: "Intellectual property", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safeproject-intellectualproperty" + }, { + questionSets: [{ + questionSetId: "safedata-datafields", + index: 1 + }], + navHeader: "Data fields", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safedata-datafields", + pageId: "safedata", + panelHeader: "These are the Information assets which your proposal seeks to access and use.\r\n\r\nYou should consider this definition to be wide in scope and include any source of information which you propose to access and use. The data may be highly structured or less structured in nature, already existing or to be newly collected or gathered. \r\n\r\nExamples may include national datasets, local data sets, national or local extracts from systems, national or local registries or networks, patient records, or new information to be gathered from patients, families or other cohorts. \r\n\r\nNew data” should only include data that is being specifically gathered for the first time for the purposes of this proposal. i.e. data already held in case notes and transferred to a form is not “new” data, but a survey filled out by clinicians in order to gather information not recorded anywhere else is “new”.\r" + }, { + panelId: "safedata-analysis", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Analysis", + questionSets: [{ + questionSetId: "safedata-analysis", + index: 1 + }], + panelHeader: "These are the Information assets which your proposal seeks to access and use.\r\n\r\nYou should consider this definition to be wide in scope and include any source of information which you propose to access and use. The data may be highly structured or less structured in nature, already existing or to be newly collected or gathered. \r\n\r\nExamples may include national datasets, local data sets, national or local extracts from systems, national or local registries or networks, patient records, or new information to be gathered from patients, families or other cohorts. \r\n\r\nNew data” should only include data that is being specifically gathered for the first time for the purposes of this proposal. i.e. data already held in case notes and transferred to a form is not “new” data, but a survey filled out by clinicians in order to gather information not recorded anywhere else is “new”.\r", + pageId: "safedata" + }, { + panelHeader: "This section should include information on the planned use of datasets not already included in this application. The following information is required:\r\n\r\nA descriptive name so that it is clear what the dataset is. \r\n\r\nSufficient information to explain the content of the dataset. \r\n\r\nWhether the proposal requires linkage of data, the use of matched controls, or the extraction of anonymised data.\r\n\r\nPlease indicate which organisation or body is undertaking these processes and which variables from the data sources requested will be used to achieve the proposed linkage. This should cover every dataset and variable you will require.\r\n", + pageId: "safedata", + panelId: "safedata-otherdatasetsintentiontolinkdata", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Other datasets - Intention to link data", + questionSets: [{ + index: 1, + questionSetId: "safedata-otherdatasetsintentiontolinkdata" + }] + }, { + questionSets: [{ + questionSetId: "safedata-lawfulbasis", + index: 1 + }], + navHeader: "Lawful basis", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safedata-lawfulbasis", + pageId: "safedata", + panelHeader: "General Data Protection Regulation (GDPR) applies to ‘controllers’ and ‘processors’. \r\n\r\nA controller determines the purposes and means of processing personal data.\r\n\r\nA processor is responsible for processing personal data on behalf of a controller.\r\n \r\nGDPR applies to processing carried out by organisations operating within the EU. It also applies to organisations outside the EU that offer goods or services to individuals in the EU.\r\nGDPR does not apply to certain activities including processing covered by the Law Enforcement Directive, processing for national security purposes and processing carried out by individuals purely for personal/household activities. \r\n \r\nGDPR only applies to information which relates to an identifiable living individual. Information relating to a deceased person does not constitute personal data and therefore is not subject to the GDPR.\r" + }, { + questionSets: [{ + index: 1, + questionSetId: "safedata-confidentialityavenue" + }], + navHeader: "Confidentiality avenue", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safedata-confidentialityavenue", + pageId: "safedata", + panelHeader: "If confidential information is being disclosed, the organisations holding this data (both the organisation disclosing the information and the recipient organisation) must also have a lawful basis to hold and use this information, and if applicable, have a condition to hold and use special categories of confidential information, and be fair and transparent about how they hold and use this data. \r\n\r\nIn England and Wales, if you are using section 251 of the NHS Act 2006 (s251) as a legal basis for identifiable data, you will need to ensure that you have the latest approval letter and application. \r\n\r\nFor Scotland this application will be reviewed by the Public Benefit and Privacy Panel.\r\n\r\nIn Northern Ireland it will be considered by the Privacy Advisory Committee. If you are using patient consent as the legal basis, you will need to provide all relevant consent forms and information leaflets.\r\n" + }, { + panelHeader: "This section details the research and ethics approval which you have obtained or sought for your project, or otherwise provides evidence as to why such approval is not necessary. \r\nWhere such approval is not in place, it is important that you demonstrate why this is the case and provide assurances if approval is pending. If you need advice on whether ethics approval is necessary, you should approach your local ethics services in the first instance. Information about UK research ethics committees and ethical opinions can be found on the Health Research Authority website.\r\n", + pageId: "safedata", + navHeader: "Ethical approval", + questionSets: [{ + index: 1, + questionSetId: "safedata-ethicalapproval" + }], + panelId: "safedata-ethicalapproval", + questionPanelHeaderText: "TODO: We need a description for this panel" + }, { + panelHeader: "This section details in what way the proposal aims to store and use data, and controls in place to minimise risks associated with this storage and use. If you have indicated that your proposal seeks to store and use data exclusively through a recognised trusted research environment, then you do not need to complete this section.\r\n \r\nIn relation to personal data, means any operation or set of operations which is performed on personal data or on sets of personal data (whether or not by automated means, such as collection, recording, organisation, structuring, storage, alteration, retrieval, consultation, use, disclosure, dissemination, restriction, erasure or destruction).\r\n \r\nAll Locations where processing will be undertaken, for the avoidance of doubt storage is considered processing. For each separate organisation processing data which is not fully anonymous a separate partner organisation form must also be completed.\r\n \r\n Processing, in relation to information or data means obtaining, recording or holding the information or data or carrying out any operation or set of operations on the information or data, including—\r\n a) organisation, adaptation or alteration of the information or data,\r\n b) retrieval, consultation or use of the information or data,\r\n c) disclosure of the information or data by transmission,\r\n dissemination or otherwise making available, or\r\n d) alignment, combination, blocking, erasure or destruction of the information or data.\r\n\r\nPlease use the file upload function if you're not able to add all organisations via the form.\r", + pageId: "safesettings", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safesettings-storageandprocessing", + questionSets: [{ + questionSetId: "safesettings-storageandprocessing", + index: 1 + }], + navHeader: "Storage and processing" + }, { + pageId: "safesettings", + panelHeader: "", + panelId: "safesettings-dataflow", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Dataflow", + questionSets: [{ + index: 1, + questionSetId: "safesettings-dataflow" + }] + }, { + panelId: "safeoutputs-outputsdisseminationplans", + questionPanelHeaderText: "TODO: We need a description for this panel", + navHeader: "Outputs dissemination plans", + questionSets: [{ + index: 1, + questionSetId: "safeoutputs-outputsdisseminationplans" + }], + panelHeader: "Please include any plans for dissemination and publication of the data and results arising from your proposal. Please also specify any controls in place to minimise risks associated with publication. Dissemination can take place in a variety of ways and through many mechanisms, including through electronic media, print media or word of mouth.", + pageId: "safeoutputs" + }, { + navHeader: "Retention", + questionSets: [{ + questionSetId: "safeoutputs-retention", + index: 1 + }], + panelId: "safeoutputs-retention", + questionPanelHeaderText: "TODO: We need a description for this panel", + pageId: "safeoutputs", + panelHeader: "This section details how the project will treat data being processed after it has been used for the purpose of the proposal outlined, including governance in place to determine how long it will be retained, and controls to manage its subsequent disposal if required. Please reference any relevant policies and procedures which are in place to govern retention and disposal of data as outlined in the proposal." + }, { + panelHeader: "This section details how the project will treat data being processed after it has been used for the purpose of the proposal outlined, including governance in place to determine how long it will be retained, and controls to manage its subsequent disposal if required. Please reference any relevant policies and procedures which are in place to govern retention and disposal of data as outlined in the proposal.", + pageId: "safeoutputs", + questionPanelHeaderText: "TODO: We need a description for this panel", + panelId: "safeoutputs-archiving", + questionSets: [{ + questionSetId: "safeoutputs-archiving", + index: 1 + }], + navHeader: "Archiving" + }], + questionSets: [{ + questionSetHeader: "Archiving", + questions: [{ + validations: [{ + type: "isLength", + message: "Please enter a value", + params: [1] + }], + input: { + required: true, + type: "textareaInput" + }, + question: "What method of destruction will be used when this period has expired?", + guidance: "Please provide details of how the data/files will be disposed of at the end of the period specified above. You might refer to any relevant disposal or destruction policies held by your organisation, by summarising the relevant section from the policy or including a URL and indicating which section is relevant.", + questionId: "safeoutputsdataretentiondestructionmethod" + }, { + questionId: "safeoutputsdataretentiondestructionevidence", + input: { + type: "textareaInput" + }, + guidance: "Please confirm you will notify us when the data have been destroyed. ", + question: "What evidence will be provided that destruction has occurred and when?" + }], + questionSetId: "safeoutputs-archiving" + }] + }, + schemaId: "5fbabae775c2095bdbdc1533", + files: [], + amendmentIterations: [], + createdAt: "2022-01-26T09:28:16.463Z", + updatedAt: "2022-01-26T09:28:16.586Z", + __v: 0, + projectId: "61F1-1430-7839-7B35-0634-DAC3", + versionTree: { + 1.0: { + applicationId: "61f1143078397b350634dac3", + displayTitle: "Version 1.0", + detailedTitle: "Version 1.0", + link: "/data-access-request/61f1143078397b350634dac3?version=1.0", + applicationType: "initial", + applicationStatus: "inProgress", + isShared: false + } + } + } +]; \ No newline at end of file diff --git a/src/resources/user/__mocks__/tools.data.js b/src/resources/user/__mocks__/tools.data.js index d46afb1b..fcbb6883 100644 --- a/src/resources/user/__mocks__/tools.data.js +++ b/src/resources/user/__mocks__/tools.data.js @@ -1,118 +1,106 @@ export const mockTools = [ { - "_id": { - "$oid": "618a6dfef53d81176faa3828" + _id: "618a6dfef53d81176faa3828", + categories: { + programmingLanguage: [] }, - "categories": { - "programmingLanguage": [] + tags: { + features: [], + topics: [] }, - "tags": { - "features": [], - "topics": [] + document_links: { + doi: [], + pdf: [], + html: [] }, - "document_links": { - "doi": [], - "pdf": [], - "html": [] + datasetfields: { + geographicCoverage: [], + physicalSampleAvailability: [], + technicaldetails: [], + versionLinks: [], + phenotypes: [] }, - "datasetfields": { - "geographicCoverage": [], - "physicalSampleAvailability": [], - "technicaldetails": [], - "versionLinks": [], - "phenotypes": [] - }, - "authors": [], - "emailNotifications": true, - "showOrganisation": true, - "structuralMetadata": [], - "datasetVersionIsV1": false, - "isCohortDiscovery": false, - "toolids": [], - "datasetids": [], - "id": 9285155117905740, - "type": "person", - "firstname": "Joseph", - "lastname": "Best", - "bio": "", - "link": "", - "orcid": "https://orcid.org/", - "activeflag": "active", - "terms": true, - "sector": "Charity/Non-profit", - "organisation": "", - "showSector": true, - "showBio": true, - "showLink": true, - "showOrcid": true, - "showDomain": true, - "profileComplete": true, - "relatedObjects": [], - "programmingLanguage": [], - "createdAt": { - "$date": "2021-11-09T12:47:58.360Z" - }, - "updatedAt": { - "$date": "2021-11-09T12:47:58.360Z" - }, - "__v": 0 + authors: [8470291714590256], + emailNotifications: true, + showOrganisation: true, + structuralMetadata: [], + datasetVersionIsV1: false, + isCohortDiscovery: false, + toolids: [], + datasetids: [], + id: 9285155117905740, + type: "person", + firstname: "Joseph", + lastname: "Best", + bio: "", + link: "", + orcid: "https://orcid.org/", + activeflag: "active", + terms: true, + sector: "Charity/Non-profit", + organisation: "", + showSector: true, + showBio: true, + showLink: true, + showOrcid: true, + showDomain: true, + profileComplete: true, + relatedObjects: [], + programmingLanguage: [], + createdAt: "2021-11-09T12:47:58.360Z", + updatedAt: "2021-11-09T12:47:58.360Z", + __v: 0 }, { - "_id": { - "$oid": "619390f4619256bde461d2a3" - }, - "categories": { - "programmingLanguage": [] - }, - "tags": { - "features": [], - "topics": [] - }, - "document_links": { - "doi": [], - "pdf": [], - "html": [] + _id: "619390f4619256bde461d2a3", + categories: { + programmingLanguage: [] }, - "datasetfields": { - "geographicCoverage": [], - "physicalSampleAvailability": [], - "technicaldetails": [], - "versionLinks": [], - "phenotypes": [] + tags: { + features: [], + topics: [] }, - "authors": [], - "emailNotifications": true, - "showOrganisation": true, - "structuralMetadata": [], - "datasetVersionIsV1": false, - "isCohortDiscovery": false, - "toolids": [], - "datasetids": [], - "id": 9248830212478532, - "type": "person", - "firstname": "HDRUK", - "lastname": "Developer", - "bio": "", - "link": "", - "orcid": "https://orcid.org/0000-0002-7770-8811", - "activeflag": "active", - "terms": true, - "sector": "Charity/Non-profit", - "organisation": "", - "showSector": true, - "showBio": true, - "showLink": true, - "showOrcid": true, - "showDomain": true, - "profileComplete": true, - "relatedObjects": [], - "programmingLanguage": [], - "createdAt": { - "$date": "2021-11-16T11:07:32.009Z" + document_links: { + doi: [], + pdf: [], + html: [] }, - "updatedAt": { - "$date": "2021-11-16T11:07:32.009Z" + datasetfields: { + geographicCoverage: [], + physicalSampleAvailability: [], + technicaldetails: [], + versionLinks: [], + phenotypes: [] }, - "__v": 0 + authors: [8470291714590257], + emailNotifications: true, + showOrganisation: true, + structuralMetadata: [], + datasetVersionIsV1: false, + isCohortDiscovery: false, + toolids: [], + datasetids: [], + id: 9248830212478532, + type: "person", + firstname: "HDRUK", + lastname: "Developer", + bio: "", + link: "", + orcid: "https://orcid.org/0000-0002-7770-8811", + activeflag: "active", + terms: true, + sector: "Charity/Non-profit", + organisation: "", + showSector: true, + showBio: true, + showLink: true, + showOrcid: true, + showDomain: true, + profileComplete: true, + relatedObjects: [], + programmingLanguage: [], + createdAt: "2021-11-16T11:07:32.009Z", + updatedAt: "2021-11-16T11:07:32.009Z", + __v: 0 } ]; \ No newline at end of file diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js index 8efdb6bd..6f8affe6 100644 --- a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js +++ b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js @@ -14,10 +14,10 @@ afterAll(async () => { await dbHandler.closeDatabase(); }); -describe('getCollaboratorsCohorts should return one value', () => { +describe('getCollaboratorsCohorts tests', () => { it('should return values', async () => { const currentUserId = 8470291714590257; - const filter = currentUserId ? {} : { uploaders: currentUserId }; + const filter = currentUserId ? { uploaders: currentUserId } : {}; const result = await getCollaboratorsCohorts(filter, currentUserId); expect(result.length > 0).toBe(true); @@ -26,11 +26,11 @@ describe('getCollaboratorsCohorts should return one value', () => { it('should return values', async () => { const currentUserId = null; - const filter = currentUserId ? {} : { uploaders: currentUserId }; + const filter = currentUserId ? { uploaders: currentUserId } : {}; const result = await getCollaboratorsCohorts(filter, currentUserId); console.log(`result : ${JSON.stringify(result)}`); - // expect(result.length > 0).toBe(true); + expect(result.length > 0).toBe(true); expect(typeof result).toBe('object'); }); }); \ No newline at end of file diff --git a/src/resources/user/__tests__/getCollaboratorsCollections.test.js b/src/resources/user/__tests__/getCollaboratorsCollections.test.js index 70458de3..63a069ca 100644 --- a/src/resources/user/__tests__/getCollaboratorsCollections.test.js +++ b/src/resources/user/__tests__/getCollaboratorsCollections.test.js @@ -14,7 +14,7 @@ afterAll(async () => { await dbHandler.closeDatabase(); }); -describe('getUniqueCollaborators should return one value', () => { +describe('getCollaboratorsCollections tests', () => { it('should return values', async () => { const currentUserId = 8470291714590257; const filter = currentUserId ? { authors: currentUserId } : {}; diff --git a/src/resources/user/__tests__/getCollaboratorsDARs.test.js b/src/resources/user/__tests__/getCollaboratorsDARs.test.js new file mode 100644 index 00000000..e8ce0e14 --- /dev/null +++ b/src/resources/user/__tests__/getCollaboratorsDARs.test.js @@ -0,0 +1,36 @@ +import dbHandler from '../../../config/in-memory-db'; +import {mockDars} from '../__mocks__/dars.data'; + +const {getCollaboratorsDARs} = require('../user.service'); + + +beforeAll(async () => { + await dbHandler.connect(); + await dbHandler.loadData({ data_requests: mockDars }); +}); + +afterAll(async () => { + await dbHandler.clearDatabase(); + await dbHandler.closeDatabase(); +}); + +describe('getCollaboratorsDARs tests', () => { + it('should return values', async () => { + const currentUserId = 8470291714590257; + const filter = currentUserId ? { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] } : {}; + + const result = await getCollaboratorsDARs(filter, currentUserId); + expect(result.length > 0).toBe(true); + expect(typeof result).toBe('object'); + }); + + it('should return values', async () => { + const currentUserId = null; + const filter = currentUserId ? { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] } : {}; + + const result = await getCollaboratorsDARs(filter, currentUserId); + console.log(`result : ${JSON.stringify(result)}`); + expect(result.length > 0).toBe(true); + expect(typeof result).toBe('object'); + }); +}); \ No newline at end of file diff --git a/src/resources/user/__tests__/getCollaboratorsTools.test.js b/src/resources/user/__tests__/getCollaboratorsTools.test.js new file mode 100644 index 00000000..63ce9a71 --- /dev/null +++ b/src/resources/user/__tests__/getCollaboratorsTools.test.js @@ -0,0 +1,35 @@ +import dbHandler from '../../../config/in-memory-db'; +import {mockTools} from '../__mocks__/tools.data'; + +const {getCollaboratorsTools} = require('../user.service'); + + +beforeAll(async () => { + await dbHandler.connect(); + await dbHandler.loadData({ tools: mockTools }); +}); + +afterAll(async () => { + await dbHandler.clearDatabase(); + await dbHandler.closeDatabase(); +}); + +describe('getCollaboratorsTools tests', () => { + it('should return values', async () => { + const currentUserId = 8470291714590257; + const filter = currentUserId ? { uploaders: currentUserId } : {}; + + const result = await getCollaboratorsTools(filter, currentUserId); + expect(typeof result).toBe('object'); + }); + + it('should return values', async () => { + const currentUserId = null; + const filter = currentUserId ? { uploaders: currentUserId } : {}; + + const result = await getCollaboratorsTools(filter, currentUserId); + console.log(`result : ${JSON.stringify(result)}`); + expect(result.length > 0).toBe(true); + expect(typeof result).toBe('object'); + }); +}); \ No newline at end of file diff --git a/src/resources/user/__tests__/getUniqueCollaborators.test.js b/src/resources/user/__tests__/getUniqueCollaborators.test.js index 0c8ebb40..bd8da7f2 100644 --- a/src/resources/user/__tests__/getUniqueCollaborators.test.js +++ b/src/resources/user/__tests__/getUniqueCollaborators.test.js @@ -1,7 +1,7 @@ const {getUniqueCollaborators} = require('../user.service'); -describe('getUniqueCollaborators', () => { +describe('getUniqueCollaborators tests', () => { it('should return a unique collaborator like map', () => { let collaborators = [39025048818527176,917335621870613]; const result = getUniqueCollaborators(collaborators); diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index f24b5063..3dd25793 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -90,23 +90,20 @@ export const getUsersCollaborators = async (currentUserId) => { let filter = null; filter = currentUserId ? { authors: currentUserId } : {}; - + // Get all collaborators from collections await getCollaboratorsCollections(filter, currentUserId); - filter = currentUserId ? {} : { uploaders: currentUserId }; - // Get all collaborators from cohorts + filter = currentUserId ? { uploaders: currentUserId } : {}; await getCollaboratorsCohorts(filter, currentUserId); // Get all collaborators from tools and papers (data collection) + filter = currentUserId ? { authors: currentUserId } : {}; await getCollaboratorsTools(filter, currentUserId); - if (currentUserId) { - filter = { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }; - } - // Get all collaborators from DARs + filter = currentUserId ? { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] } : {}; await getCollaboratorsDARs(filter, currentUserId); // Strip out duplicate collaborators, add a count @@ -151,16 +148,13 @@ export const getUniqueCollaborators = (collaborators) => { } export const populateCollaborators = async (collaboratorsEntity, items, currentUserId) => { - console.log(`collaboratorsEntity: ${JSON.stringify(collaboratorsEntity)}`); for (const collaborator of collaboratorsEntity) { if ((!currentUserId && items === 'authorIds') || (currentUserId && items === 'authorIds' && arrCollaborators.userId !== currentUserId)) { arrCollaborators.push(collaborator.userId); } - console.log(`collaborator[items]: ${JSON.stringify(collaborator[items])}`); for (const item of collaborator[items]) { - console.log(item); if (!currentUserId || (currentUserId && item !== currentUserId)) { arrCollaborators.push(item); } From 6958614a5ba6accfb01c697c8f4d1ec3255134c7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 10 Mar 2022 17:47:35 +0000 Subject: [PATCH 044/389] new tests --- src/resources/user/__tests__/getCollaboratorsCohorts.test.js | 1 - src/resources/user/__tests__/getCollaboratorsDARs.test.js | 1 - src/resources/user/__tests__/getCollaboratorsTools.test.js | 1 - 3 files changed, 3 deletions(-) diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js index 6f8affe6..bb626688 100644 --- a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js +++ b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js @@ -29,7 +29,6 @@ describe('getCollaboratorsCohorts tests', () => { const filter = currentUserId ? { uploaders: currentUserId } : {}; const result = await getCollaboratorsCohorts(filter, currentUserId); - console.log(`result : ${JSON.stringify(result)}`); expect(result.length > 0).toBe(true); expect(typeof result).toBe('object'); }); diff --git a/src/resources/user/__tests__/getCollaboratorsDARs.test.js b/src/resources/user/__tests__/getCollaboratorsDARs.test.js index e8ce0e14..048a8e68 100644 --- a/src/resources/user/__tests__/getCollaboratorsDARs.test.js +++ b/src/resources/user/__tests__/getCollaboratorsDARs.test.js @@ -29,7 +29,6 @@ describe('getCollaboratorsDARs tests', () => { const filter = currentUserId ? { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] } : {}; const result = await getCollaboratorsDARs(filter, currentUserId); - console.log(`result : ${JSON.stringify(result)}`); expect(result.length > 0).toBe(true); expect(typeof result).toBe('object'); }); diff --git a/src/resources/user/__tests__/getCollaboratorsTools.test.js b/src/resources/user/__tests__/getCollaboratorsTools.test.js index 63ce9a71..a083609d 100644 --- a/src/resources/user/__tests__/getCollaboratorsTools.test.js +++ b/src/resources/user/__tests__/getCollaboratorsTools.test.js @@ -28,7 +28,6 @@ describe('getCollaboratorsTools tests', () => { const filter = currentUserId ? { uploaders: currentUserId } : {}; const result = await getCollaboratorsTools(filter, currentUserId); - console.log(`result : ${JSON.stringify(result)}`); expect(result.length > 0).toBe(true); expect(typeof result).toBe('object'); }); From b3c749c16d320c7b7a74ca811061092916fc2121 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 11 Mar 2022 16:56:12 +0000 Subject: [PATCH 045/389] reconfigure search pipeline --- src/resources/dataUseRegister/dataUseRegister.controller.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index 28ac7bc4..f2074714 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -267,8 +267,6 @@ export default class DataUseRegisterController extends Controller { } let searchQuery = { $and: [{ activeflag: 'active' }] }; - if (searchString.length > 0) searchQuery['$and'].push({ $text: { $search: searchString } }); - searchQuery = getObjectFilters(searchQuery, req, 'dataUseRegister'); const aggregateQuery = [ @@ -348,6 +346,10 @@ export default class DataUseRegisterController extends Controller { { $match: searchQuery }, ]; + if (searchString.length > 0) { + aggregateQuery.unshift({ $match: { $text: { $search: searchString } } }); + } + const result = await DataUseRegister.aggregate(aggregateQuery); return res.status(200).json({ success: true, result }); From f476a153b9387d266ff6058b4bab6aa6e365edd8 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Sun, 13 Mar 2022 10:02:51 +0000 Subject: [PATCH 046/389] add data use controller tests --- .../__mocks__/dataUseRegisters.js | 95 +++++++++++++++++++ .../dataUseRegister.controller.test.js | 65 +++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/resources/dataUseRegister/__tests__/dataUseRegister.controller.test.js diff --git a/src/resources/dataUseRegister/__mocks__/dataUseRegisters.js b/src/resources/dataUseRegister/__mocks__/dataUseRegisters.js index 1d65fb54..d624535d 100644 --- a/src/resources/dataUseRegister/__mocks__/dataUseRegisters.js +++ b/src/resources/dataUseRegister/__mocks__/dataUseRegisters.js @@ -444,3 +444,98 @@ export const editedDataUseObject = { researchOutputs: 'research Outputs', relatedObjects: [], }; + +export const dataUseRegistersStub = [ + { + _id: 'test_id', + counter: 1, + keywords: ['Blood', 'Test'], + datasetTitles: ['HDR UK Papers & Preprints'], + gatewayDatasets: ['1ccb9da3-4b5e-4b4b-a142-af739037983c'], + nonGatewayDatasets: [], + gatewayApplicants: [], + nonGatewayApplicants: ['John Smith'], + fundersAndSponsors: [], + otherApprovalCommittees: [], + gatewayOutputsTools: [514564326145692], + gatewayOutputsPapers: [7485531672584456], + nonGatewayOutputs: ['www.yahoo.com'], + publisher: 'testPub', + projectIdText: '619B-73C4-BCB0-7C22-D168-4DAC', + projectId: 'testId', + applicantId: '', + accreditedResearcherStatus: 'Unknown', + projectTitle: 'This is the title for Pauls Project', + organisationName: 'PA Consulting', + laySummary: 'This is the lay summary for Pauls Project', + publicBenefitStatement: 'This is how my project benefit the public and what is the anticipated impact', + dutyOfConfidentiality: 'Informed consent', + datasetLinkageDescription: '', + requestFrequency: 'One-off', + legalBasisForDataArticle6: + '(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;', + legalBasisForDataArticle9: + '(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;', + privacyEnhancements: 'These are the steps', + latestApprovalDate: '2021-11-22T11:04:03.288Z', + relatedObjects: [], + activeflag: 'active', + user: 'userId', + userName: 'Paul McCafferty', + updatedon: '2021-12-17T10:54:39.812Z', + lastActivity: '2021-12-17T10:54:39.812Z', + manualUpload: false, + id: 17931311209279556, + type: 'dataUseRegister', + createdAt: '2021-11-22T11:04:03.507Z', + updatedAt: '2021-12-17T10:54:39.815Z', + __v: 0, + accessType: 'Release', + }, + { + _id: 'test_id2', + counter: 1, + keywords: ['Blood', 'Test'], + datasetTitles: ['HDR UK Papers & Preprints'], + gatewayDatasets: ['1ccb9da3-4b5e-4b4b-a142-af739037983c'], + nonGatewayDatasets: [], + gatewayApplicants: [], + nonGatewayApplicants: ['John Smith'], + fundersAndSponsors: [], + otherApprovalCommittees: [], + gatewayOutputsTools: [514564326145692], + gatewayOutputsPapers: [7485531672584456], + nonGatewayOutputs: ['www.yahoo.com'], + publisher: 'testPub', + projectIdText: '619B-73C4-BCB0-7C22-D168-4DAC', + projectId: 'testId2', + applicantId: '', + accreditedResearcherStatus: 'Unknown', + projectTitle: 'This is the title for Pauls Second Project', + organisationName: 'PA Consulting', + laySummary: 'This is the lay summary for Pauls Second Project', + publicBenefitStatement: 'This is how my project benefit the public and what is the anticipated impact', + dutyOfConfidentiality: 'Informed consent', + datasetLinkageDescription: '', + requestFrequency: 'One-off', + legalBasisForDataArticle6: + '(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;', + legalBasisForDataArticle9: + '(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;', + privacyEnhancements: 'These are the steps', + latestApprovalDate: '2021-11-22T11:04:03.288Z', + relatedObjects: [], + activeflag: 'active', + user: 'userId', + userName: 'Paul McCafferty', + updatedon: '2021-12-17T10:54:39.812Z', + lastActivity: '2021-12-17T10:54:39.812Z', + manualUpload: false, + id: 17931311209279556, + type: 'dataUseRegister', + createdAt: '2021-11-22T11:04:03.507Z', + updatedAt: '2021-12-17T10:54:39.815Z', + __v: 0, + accessType: 'Release', + }, +]; diff --git a/src/resources/dataUseRegister/__tests__/dataUseRegister.controller.test.js b/src/resources/dataUseRegister/__tests__/dataUseRegister.controller.test.js new file mode 100644 index 00000000..0302cb17 --- /dev/null +++ b/src/resources/dataUseRegister/__tests__/dataUseRegister.controller.test.js @@ -0,0 +1,65 @@ +import sinon from 'sinon'; +import mongoose from 'mongoose'; + +import dbHandler from '../../../config/in-memory-db'; +import { dataUseRegistersStub } from '../__mocks__/dataUseRegisters'; +import DataUseRegisterController from '../dataUseRegister.controller'; + +beforeAll(async () => { + await dbHandler.connect(); + await dbHandler.loadData({ datauseregisters: dataUseRegistersStub }); + await mongoose.connection + .collection('datauseregisters') + .createIndex({ datasetTitles: 'text', fundersAndSponsors: 'text', keywords: 'text', laySummary: 'text', projectTitle: 'text' }); +}); + +afterEach(() => { + sinon.restore(); +}); + +afterAll(async () => { + await dbHandler.closeDatabase(); +}); + +describe('CLASS: dataUseRegisterController', () => { + const dataUseRegisterController = new DataUseRegisterController(); + + const mockedRequest = () => { + const req = { + query: {}, + params: {}, + }; + return req; + }; + + const mockedResponse = () => { + const res = {}; + res.status = jest.fn().mockReturnValue(res); + res.json = jest.fn().mockReturnValue(res); + return res; + }; + + describe('METHOD: searchDataUseRegisters', () => { + it('TEST: it should return a 200 response and 2 DURs if no search string is given', async () => { + const req = mockedRequest(); + const res = mockedResponse(); + + await dataUseRegisterController.searchDataUseRegisters(req, res); + + expect(res.json.mock.calls[0][0].result.length).toBe(2); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('TEST: it should filter the results appropriately based on a free text search term', async () => { + const req = mockedRequest(); + const res = mockedResponse(); + + req.query.search = 'second'; + + await dataUseRegisterController.searchDataUseRegisters(req, res); + + expect(res.json.mock.calls[0][0].result.length).toBe(1); + expect(res.status).toHaveBeenCalledWith(200); + }); + }); +}); From 89d24f8e07e7b67217f536f30e8e28eef02d66c1 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 14 Mar 2022 09:48:03 +0000 Subject: [PATCH 047/389] update search algorithm --- src/resources/search/search.router.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/resources/search/search.router.js b/src/resources/search/search.router.js index cd8a461d..82bca413 100644 --- a/src/resources/search/search.router.js +++ b/src/resources/search/search.router.js @@ -29,17 +29,7 @@ router.get('/', async (req, res) => { let searchAll = false; if (searchString.length > 0) { - searchQuery['$and'].push({ $text: { $search: searchString } }); - - /* datasetSearchString = '"' + searchString.split(' ').join('""') + '"'; - //The following code is a workaround for the way search works TODO:work with MDC to improve API - if (searchString.match(/"/)) { - //user has added quotes so pass string through - datasetSearchString = searchString; - } else { - //no quotes so lets a proximiy search - datasetSearchString = '"'+searchString+'"~25'; - } */ + searchQuery['$and'].push({ $text: { $search: `'${searchString.split(" ").map(item => item.replace(/^/,"\"").replace(/$/,"\"")).join(" ")}'` }}); } else { searchAll = true; } From 8ea5da97b010ad235b6bc2ec7b94d5223c36442a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 15 Mar 2022 13:58:29 +0000 Subject: [PATCH 048/389] update for bug --- src/resources/user/user.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 7c363c6d..97b78ece 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -6,7 +6,7 @@ import { Cohort } from '../cohort/cohort.model'; import { Collections } from '../collections/collections.model'; import { DataRequestModel } from '../datarequest/datarequest.model'; -const arrCollaborators = []; +let arrCollaborators = []; export async function createUser({ firstname, lastname, email, providerId, provider, role }) { return new Promise(async resolve => { @@ -166,6 +166,7 @@ export const populateCollaborators = async (collaboratorsEntity, items, currentU export const getUsers = async (currentUserId) => { // Get the users collaborators + arrCollaborators = []; let usersCollaborators = await getUsersCollaborators(currentUserId); // Get the whole list of users From 2b9e119b0436bc8309ba0204ab7e676c6751ac72 Mon Sep 17 00:00:00 2001 From: kandaj Date: Wed, 16 Mar 2022 15:09:26 +0000 Subject: [PATCH 049/389] date format issue fix --- src/utils/__tests__/datasetonboarding.util.test.js | 14 ++++++++++++++ src/utils/datasetonboarding.util.js | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/datasetonboarding.util.test.js b/src/utils/__tests__/datasetonboarding.util.test.js index 87521246..6e8e6fde 100644 --- a/src/utils/__tests__/datasetonboarding.util.test.js +++ b/src/utils/__tests__/datasetonboarding.util.test.js @@ -103,4 +103,18 @@ describe('Dataset onboarding utility', () => { }); }); }); + describe('returnAsDate', () => { + it('Should return a correctly formatted date for `2007-01-04`', () => { + expect(datasetonboardingUtil.returnAsDate('2007-01-04')).toStrictEqual(`04/01/2007`); + }); + it('Should return a correctly formatted date for `2007/01/04`', () => { + expect(datasetonboardingUtil.returnAsDate('2007/01/04')).toStrictEqual(`04/01/2007`); + }); + it('Should not return a correctly formatted date for `01-04-2007`', () => { + expect(datasetonboardingUtil.returnAsDate('04-01-2007')).not.toEqual(`04/01/2007`); + }); + it('Should not return a correctly formatted date for `01/04/2007`', () => { + expect(datasetonboardingUtil.returnAsDate('04/01/2007')).not.toEqual(`04/01/2007`); + }); + }); }); diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 51f09e84..1b629d1a 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -231,7 +231,6 @@ const returnAsArray = value => { * @returns {String} [value as date format] */ const returnAsDate = value => { - if (moment(value, 'DD/MM/YYYY').isValid()) return value; return moment(new Date(value)).format('DD/MM/YYYY'); }; @@ -1300,4 +1299,5 @@ export default { buildBulkUploadObject, buildv2Object, datasetv2ObjectComparison, + returnAsDate, }; From 09988addf082cfe32af78317a2536e10530a5ba6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 16 Mar 2022 16:16:41 +0000 Subject: [PATCH 050/389] bug fix for checking permissions in datasetonboarding --- src/utils/datasetonboarding.util.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 51f09e84..c2a01ac9 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -885,7 +885,10 @@ const createNotifications = async (type, context) => { team = await TeamModel.findOne({ _id: context.datasetv2.summary.publisher.identifier }).lean(); for (let member of team.members) { - if (member.roles.some(role => ['manager', 'metadata_editor'].includes(role))) 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 } }) From f46028717f7cc8f95b05accc501997429a59fbaa Mon Sep 17 00:00:00 2001 From: kandaj Date: Tue, 22 Mar 2022 16:13:54 +0000 Subject: [PATCH 051/389] update structural metadata --- .../datasetonboarding.controller.js | 2 +- src/services/__mocks__/datasetSearchStub.js | 16 ++++++ .../datasetonboarding.service.test.js | 49 +++++++++++++++++++ src/services/datasetonboarding.service.js | 2 +- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/controllers/datasetonboarding.controller.js b/src/controllers/datasetonboarding.controller.js index 759a18ae..6a8ce5a7 100644 --- a/src/controllers/datasetonboarding.controller.js +++ b/src/controllers/datasetonboarding.controller.js @@ -158,7 +158,7 @@ export default class DatasetOnboardingController { if (isEmpty(structuralMetadata)) { return res.status(404).json({ status: 'error', message: 'Update failed' }); } else { - await this.datasetonboardingService.updateStructuralMetadata(structuralMetadata, id); + await this.datasetonboardingService.updateStructuralMetadata(structuralMetadata, data.percentageCompleted, id); return res.status(200).json(); } } diff --git a/src/services/__mocks__/datasetSearchStub.js b/src/services/__mocks__/datasetSearchStub.js index d5fc5d05..3e7460cb 100644 --- a/src/services/__mocks__/datasetSearchStub.js +++ b/src/services/__mocks__/datasetSearchStub.js @@ -78,6 +78,22 @@ export const datasetSearchStub = [ }, }, type: 'dataset', + structuralMetadata: [], + percentageCompleted: { + summary: 20, + documentation: 100, + coverage: 0, + origin: 0, + temporal: 0, + usage: 0, + access: 0, + formatAndStandards: 0, + enrichmentAndLinkage: 0, + observations: 0, + provenance: 0, + accessibility: 0, + structural: 0, + }, }, { _id: ObjectID('615aee882414847722e46ac5'), diff --git a/src/services/__tests__/datasetonboarding.service.test.js b/src/services/__tests__/datasetonboarding.service.test.js index f3327993..62bf39a9 100644 --- a/src/services/__tests__/datasetonboarding.service.test.js +++ b/src/services/__tests__/datasetonboarding.service.test.js @@ -283,4 +283,53 @@ describe('datasetOnboardingService', () => { expect(duplicatedDataset.datasetVersion).toEqual('1.0.0'); }); }); + + describe('updateStructuralMetadata', () => { + it('structuralMetadata should be empty for a dataset and percentageCompleted to be zero', async () => { + const dataset = datasetSearchStub[3]; + expect(dataset.structuralMetadata).toEqual([]); + expect(dataset.percentageCompleted.structural).toEqual(0); + }); + it('structuralMetadata should not be empty for a dataset and percentageCompleted to be 100 ', async () => { + const dataset = datasetSearchStub[3]; + const structuralMetadata = [ + { + tableName: 'PDR28DrugAdmin', + tableDescription: 'Drug Admin details for each spell', + columnName: 'drug_administration_id', + columnDescription: 'Unique identifier for the drug administration event', + dataType: 'varchar', + sensitive: false, + }, + { + tableName: 'PDR28DrugAdmin', + tableDescription: 'Drug Admin details for each spell', + columnName: 'patient_pseudo_id', + columnDescription: 'Hashed patient identification', + dataType: 'varchar', + sensitive: false, + }, + ]; + const percentageCompleted = { + summary: 20, + documentation: 100, + coverage: 0, + origin: 0, + temporal: 0, + usage: 0, + access: 0, + formatAndStandards: 0, + enrichmentAndLinkage: 0, + observations: 0, + provenance: 0, + accessibility: 0, + structural: 100, + }; + + await datasetonboardingService.updateStructuralMetadata(structuralMetadata, percentageCompleted, dataset._id); + const updatedDataset = await Data.findOne({ _id: dataset._id }); + expect(updatedDataset.structuralMetadata.length).toEqual(2); + expect(updatedDataset.percentageCompleted.structural).toEqual(100); + }); + }); }); diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index ec40ea23..54580148 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -363,7 +363,7 @@ export default class DatasetOnboardingService { return data; }; - updateStructuralMetadata = async (structuralMetadata, id) => { + updateStructuralMetadata = async (structuralMetadata, percentageCompleted, id) => { await Data.findByIdAndUpdate( { _id: id }, { From da301424591e4f4c8d1eb4cd5bd074cd528fe42a Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 24 Mar 2022 12:02:18 +0000 Subject: [PATCH 052/389] fix enquiry email bug --- src/resources/message/message.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index f45a32c3..a230e18a 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -30,7 +30,7 @@ module.exports = { path: 'publisher', populate: { path: 'team', - select: 'members', + select: 'members notifications', populate: { path: 'users', }, From 456f0a92a59863acb67528a3464240353a2ba78f Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 8 Apr 2022 14:36:25 +0100 Subject: [PATCH 053/389] Typo --- .../datasetonboarding.controller.js | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/src/controllers/datasetonboarding.controller.js b/src/controllers/datasetonboarding.controller.js index 9d1dcf5e..260ddabf 100644 --- a/src/controllers/datasetonboarding.controller.js +++ b/src/controllers/datasetonboarding.controller.js @@ -12,7 +12,7 @@ import { filtersService } from '../resources/filters/dependency'; import datasetonboardingUtil from '../utils/datasetonboarding.util'; import { PublisherModel } from '../resources/publisher/publisher.model'; import { activityLogService } from '../resources/activitylog/dependency'; -const HttpClient = require('../services/httpClient/httpClient') +const HttpClient = require('../services/httpClient/httpClient'); const readEnv = process.env.ENV || 'prod'; @@ -222,7 +222,7 @@ export default class DatasetOnboardingController { if (!id) { return res.status(404).json({ status: 'error', - message: 'Dataset _id could not be found.' + message: 'Dataset _id could not be found.', }); } @@ -233,7 +233,7 @@ export default class DatasetOnboardingController { if (!authorised) { return res.status(401).json({ status: 'failure', - message: 'Unauthorised' + message: 'Unauthorised', }); } @@ -248,7 +248,7 @@ export default class DatasetOnboardingController { let activityLogStatus = null; const _httpClient = new HttpClient(); - switch(applicationStatus) { + switch (applicationStatus) { case 'approved': if (userType !== constants.userTypes.ADMIN) { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); @@ -262,13 +262,16 @@ export default class DatasetOnboardingController { const publisherData = await PublisherModel.find({ _id: dataset.datasetv2.summary.publisher.identifier }).lean(); await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); - const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); - const [cookie] = responseLogin.headers["set-cookie"]; + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { + withCredentials: true, + timeout: 5000, + }); + const [cookie] = responseLogin.headers['set-cookie']; _httpClient.setHttpClientCookies(cookie); - + let jsonData = JSON.stringify(await datasetonboardingUtil.buildJSONFile(dataset)); fs.writeFileSync(__dirname + `/datasetfiles/${dataset._id}.json`, jsonData); - + var data = new FormData(); data.append('folderId', publisherData[0].mdcFolderId); data.append('importFile', fs.createReadStream(__dirname + `/datasetfiles/${dataset._id}.json`)); @@ -276,16 +279,17 @@ export default class DatasetOnboardingController { data.append('importAsNewDocumentationVersion', 'true'); const responseImport = await _httpClient.post( - metadataCatalogueLink + '/api/dataModels/import/ox.softeng.metadatacatalogue.core.spi.json/JsonImporterService/1.1', - data, + metadataCatalogueLink + '/api/dataModels/import/ox.softeng.metadatacatalogue.core.spi.json/JsonImporterService/1.1', + data, { withCredentials: true, timeout: 60000, headers: { ...data.getHeaders(), }, - }); - + } + ); + let newDatasetVersionId = responseImport.data.items[0].id; fs.unlinkSync(__dirname + `/datasetfiles/${dataset._id}.json`); @@ -293,8 +297,14 @@ export default class DatasetOnboardingController { documentationVersion: dataset.datasetVersion, }; - await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}`, updatedDatasetDetails, { withCredentials: true, timeout: 20000 }); - await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}/finalise`, null, { withCredentials: true, timeout: 20000 }); + await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}`, updatedDatasetDetails, { + withCredentials: true, + timeout: 20000, + }); + await _httpClient.put(metadataCatalogueLink + `/api/dataModels/${newDatasetVersionId}/finalise`, null, { + withCredentials: true, + timeout: 20000, + }); // Adding to DB let datasetv2Object = await datasetonboardingUtil.buildv2Object(dataset, newDatasetVersionId); @@ -380,7 +390,7 @@ export default class DatasetOnboardingController { updatedDataset = await Data.findOneAndUpdate( { _id: id }, { - activeflag: constants.datatsetStatuses.REJECTED, + activeflag: constants.datasetStatuses.REJECTED, applicationStatusDesc: applicationStatusDesc, applicationStatusAuthor: `${firstname} ${lastname}`, 'timestamps.rejected': Date.now(), @@ -393,7 +403,7 @@ export default class DatasetOnboardingController { await datasetonboardingUtil.createNotifications(constants.notificationTypes.DATASETREJECTED, updatedDataset); activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_REJECTED; - + break; case 'archive': dataset = await Data.findOne({ _id: id }).lean(); @@ -401,21 +411,27 @@ export default class DatasetOnboardingController { if (dataset.timestamps.submitted) { await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); - const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); - const [cookie] = responseLogin.headers["set-cookie"]; + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { + withCredentials: true, + timeout: 5000, + }); + const [cookie] = responseLogin.headers['set-cookie']; _httpClient.setHttpClientCookies(cookie); - await _httpClient.delete(metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, loginDetails, { withCredentials: true, timeout: 5000 }); + await _httpClient.delete(metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, loginDetails, { + withCredentials: true, + timeout: 5000, + }); await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); } updatedDataset = await Data.findOneAndUpdate( { _id: id }, - { activeflag: constants.datatsetStatuses.ARCHIVE, 'timestamps.updated': Date.now(), 'timestamps.archived': Date.now() } + { activeflag: constants.datasetStatuses.ARCHIVE, 'timestamps.updated': Date.now(), 'timestamps.archived': Date.now() } ); activityLogStatus = constants.activityLogEvents.dataset.DATASET_VERSION_ARCHIVED; - + break; case 'unarchive': dataset = await Data.findOne({ _id: id }).lean(); @@ -423,15 +439,22 @@ export default class DatasetOnboardingController { if (dataset.timestamps.submitted) { await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); - const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { withCredentials: true, timeout: 5000 }); - const [cookie] = responseLogin.headers["set-cookie"]; + const responseLogin = await _httpClient.post(metadataCatalogueLink + '/api/authentication/login', loginDetails, { + withCredentials: true, + timeout: 5000, + }); + const [cookie] = responseLogin.headers['set-cookie']; _httpClient.setHttpClientCookies(cookie); const updatedDatasetDetails = { deleted: 'false', }; - await _httpClient.put(metadataCatalogueLink + metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, updatedDatasetDetails, { withCredentials: true, timeout: 5000 }); + await _httpClient.put( + metadataCatalogueLink + metadataCatalogueLink + `/api/dataModels/${dataset.datasetid}`, + updatedDatasetDetails, + { withCredentials: true, timeout: 5000 } + ); await _httpClient.post(metadataCatalogueLink + `/api/authentication/logout`, null, { withCredentials: true, timeout: 5000 }); @@ -448,15 +471,14 @@ export default class DatasetOnboardingController { message: 'An error occurred - application status is not set correctly', }); } - + await activityLogService.logActivity(activityLogStatus, { type: constants.activityLogTypes.DATASET, updatedDataset, user: req.user, }); - + return res.status(200).json({ status: 'success' }); - } catch (err) { res.status(500).json({ status: 'error', From 5e5d5f469bb5cf4e1a1c81240e72c405fa539374 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 8 Apr 2022 14:56:49 +0100 Subject: [PATCH 054/389] Remove yarn lock --- yarn-error.log | 131 - yarn.lock | 9145 ------------------------------------------------ 2 files changed, 9276 deletions(-) delete mode 100644 yarn-error.log delete mode 100644 yarn.lock diff --git a/yarn-error.log b/yarn-error.log deleted file mode 100644 index 4c450bdc..00000000 --- a/yarn-error.log +++ /dev/null @@ -1,131 +0,0 @@ -Arguments: - C:\Program Files\nodejs\node.exe C:\Users\Peter\AppData\Roaming\npm\node_modules\yarn\bin\yarn.js - -PATH: - C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin;C:\Users\Peter\bin;C:\Program Files\Google\Chrome\Application;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\Windows\System32\OpenSSH;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Git\cmd;C:\Program Files\nodejs;C:\Program Files\Amazon\AWSCLIV2;C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0;C:\windows\System32\OpenSSH;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:\Program Files\dotnet;C:\Users\Peter\AppData\Local\Microsoft\WindowsApps;C:\Users\Peter\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\Peter\AppData\Roaming\npm - -Yarn version: - 1.22.10 - -Node version: - 12.18.4 - -Platform: - win32 x64 - -Trace: - Error: https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com/npm/react-core-ui/@google-cloud%2fmonitoring: Unauthenticated: request used expired credentials. Please renew your credentials. - at Request.params.callback [as _callback] (C:\Users\Peter\AppData\Roaming\npm\node_modules\yarn\lib\cli.js:66988:18) - at Request.self.callback (C:\Users\Peter\AppData\Roaming\npm\node_modules\yarn\lib\cli.js:140662:22) - at Request.emit (events.js:315:20) - at Request. (C:\Users\Peter\AppData\Roaming\npm\node_modules\yarn\lib\cli.js:141634:10) - at Request.emit (events.js:315:20) - at Gunzip. (C:\Users\Peter\AppData\Roaming\npm\node_modules\yarn\lib\cli.js:141556:12) - at Object.onceWrapper (events.js:421:28) - at Gunzip.emit (events.js:327:22) - at endReadableNT (_stream_readable.js:1220:12) - at processTicksAndRejections (internal/process/task_queues.js:84:21) - -npm manifest: - { - "name": "hdruk-rdt-api", - "config": { - "mongodbMemoryServer": { - "version": "5.0.0-rc7" - } - }, - "version": "0.1.1", - "private": true, - "dependencies": { - "@google-cloud/monitoring": "^2.1.0", - "@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", - "await-to-js": "^2.1.1", - "axios": "0.21.1", - "axios-retry": "^3.1.9", - "base64url": "^3.0.1", - "bcrypt": "^5.0.0", - "body-parser": "^1.19.0", - "btoa": "^1.2.1", - "cookie-parser": "^1.4.5", - "cors": "^2.8.5", - "crypto": "^1.0.1", - "crypto-js": "^4.0.0", - "csurf": "^1.11.0", - "discourse-sso": "^1.0.3", - "dotenv": "^8.2.0", - "escape-html": "^1.0.3", - "esm": "^3.2.25", - "express": "^4.17.1", - "express-rate-limit": "^5.1.3", - "express-session": "^1.17.1", - "express-validator": "^6.6.1", - "faker": "^5.3.1", - "form-data": "^3.0.0", - "googleapis": "^55.0.0", - "jose": "^2.0.2", - "jsonwebtoken": "^8.5.1", - "keygrip": "^1.1.0", - "lodash": "^4.17.19", - "mailchimp-api-v3": "^1.15.0", - "migrate-mongoose": "^4.0.0", - "moment": "^2.27.0", - "mongoose": "^5.12.7", - "morgan": "^1.10.0", - "multer": "^1.4.2", - "oidc-provider": "^6.29.3", - "passport": "^0.4.1", - "passport-azure-ad-oauth2": "0.0.4", - "passport-google-oauth": "^2.0.0", - "passport-jwt": "^4.0.0", - "passport-linkedin-oauth2": "^2.0.0", - "passport-openidconnect": "0.0.2", - "prettier": "^2.2.1", - "query-string": "^6.12.1", - "randomstring": "^1.1.5", - "sinon": "^9.2.4", - "snyk": "^1.334.0", - "swagger-ui-express": "^4.1.4", - "test": "^0.6.0", - "transformobject": "^0.3.1", - "uuid": "^8.3.1", - "yamljs": "^0.3.0" - }, - "devDependencies": { - "@babel/preset-env": "^7.12.1", - "@shelf/jest-mongodb": "^1.2.3", - "babel-jest": "^26.6.3", - "eslint": "^7.20.0", - "jest": "^26.6.3", - "mongodb-memory-server": "6.9.2", - "nodemon": "^2.0.3", - "plop": "^2.7.4", - "supertest": "^4.0.2" - }, - "scripts": { - "start": "node index.js", - "server": "nodemon index.js", - "debug": "nodemon --inspect=0.0.0.0:3001 index.js", - "build": "", - "test": "jest --runInBand", - "eject": "", - "snyk-protect": "snyk protect", - "prepublish": "npm run snyk-protect", - "prettify": "prettier --write \"src/**/*.{scss,js,jsx}\"", - "prettify-test": "prettier --write \"test/**/*.js\"" - }, - "proxy": "http://localhost:3001", - "snyk": true - } - -yarn manifest: - No manifest - -Lockfile: - No lockfile diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 6d8d4741..00000000 --- a/yarn.lock +++ /dev/null @@ -1,9145 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.15.0": - version "7.15.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" - integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== - -"@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.15.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/generator/-/generator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0" - integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== - dependencies: - "@babel/types" "^7.15.4" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz#3d0e43b00c5e49fdb6c57e421601a7a658d5f835" - integrity sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.15.4.tgz#21ad815f609b84ee0e3058676c33cf6d1670525f" - integrity sha512-P8o7JP2Mzi0SdC6eWr1zF+AEYvrsZa7GSY1lTayjF5XJhVH0kjLYUZPvTMflP7tBgZoe9gIhTa60QwFpqh/E0Q== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" - integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz#7f977c17bd12a5fba363cb19bea090394bf37d2e" - integrity sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - regexpu-core "^4.7.1" - -"@babel/helper-define-polyfill-provider@^0.2.2": - version "0.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" - integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-explode-assignable-expression@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.15.4.tgz#f9aec9d219f271eaf92b9f561598ca6b2682600c" - integrity sha512-J14f/vq8+hdC2KoWLIQSsGrC9EFBKE4NFts8pfMpymfApds+fPqR30AOUWc4tyr56h9l/GA1Sxv2q3dLZWbQ/g== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-function-name@^7.14.5", "@babel/helper-function-name@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" - integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== - dependencies: - "@babel/helper-get-function-arity" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-get-function-arity@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" - integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-hoist-variables@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" - integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-member-expression-to-functions@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" - integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" - integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.15.4": - version "7.15.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226" - integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== - dependencies: - "@babel/helper-module-imports" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-simple-access" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.6" - -"@babel/helper-optimise-call-expression@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" - integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-remap-async-to-generator@^7.14.5", "@babel/helper-remap-async-to-generator@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.15.4.tgz#2637c0731e4c90fbf58ac58b50b2b5a192fc970f" - integrity sha512-v53MxgvMK/HCwckJ1bZrq6dNKlmwlyRNYM6ypaRTdXWGOE2c1/SCa6dL/HimhPulGhZKw9W0QhREM583F/t0vQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-wrap-function" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-replace-supers@^7.14.5", "@babel/helper-replace-supers@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" - integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" - integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-skip-transparent-expression-wrappers@^7.14.5", "@babel/helper-skip-transparent-expression-wrappers@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz#707dbdba1f4ad0fa34f9114fc8197aec7d5da2eb" - integrity sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-split-export-declaration@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" - integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helper-wrap-function@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz#6f754b2446cfaf3d612523e6ab8d79c27c3a3de7" - integrity sha512-Y2o+H/hRV5W8QhIfTpRIBwl57y8PrZt6JM3V8FOo5qarjshHItyH5lXlpMfBfmBefOqSCpKZs/6Dxqp0E/U+uw== - dependencies: - "@babel/helper-function-name" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helpers@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" - integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== - dependencies: - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5": - version "7.15.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" - integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz#dbdeabb1e80f622d9f0b583efb2999605e0a567e" - integrity sha512-eBnpsl9tlhPhpI10kU06JHnrYXwg3+V6CaP2idsCXNef0aeslpqyITXQ74Vfk5uHgY7IG7XP0yIH8b42KSzHog== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.15.4" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - -"@babel/plugin-proposal-async-generator-functions@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.4.tgz#f82aabe96c135d2ceaa917feb9f5fca31635277e" - integrity sha512-2zt2g5vTXpMC3OmK6uyjvdXptbhBXfA77XGrd3gh93zwG8lZYBLOBImiGBEG0RANu3JqKEACCz5CGk73OJROBw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.15.4" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-static-block@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.15.4.tgz#3e7ca6128453c089e8b477a99f970c63fc1cb8d7" - integrity sha512-M682XWrrLNk3chXCjoPUQWOyYsB93B9z3mRyjtqqYJWDf2mfCdIYgDrA11cgNVhAQieaq6F2fn2f3wI0U4aTjA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c" - integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76" - integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb" - integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738" - integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18" - integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.15.6": - version "7.15.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.15.6.tgz#ef68050c8703d07b25af402cb96cf7f34a68ed11" - integrity sha512-qtOHo7A1Vt+O23qEAX+GdBpqaIuD3i9VRrWgCJeq7WO6H2d14EK3q11urj5Te2MAeK97nMiIdRpwd/ST4JFbNg== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.15.4" - -"@babel/plugin-proposal-optional-catch-binding@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" - integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d" - integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-private-property-in-object@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.15.4.tgz#55c5e3b4d0261fd44fe637e3f624cfb0f484e3e5" - integrity sha512-X0UTixkLf0PCCffxgu5/1RQyGGbgZuKoI+vXP4iSbJSYwPb7hu06omsFGBvQ9lJEvwgrxHdS8B5nbfcd8GyUNA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-create-class-features-plugin" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" - integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" - integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67" - integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.14.5" - -"@babel/plugin-transform-block-scoped-functions@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" - integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-block-scoping@^7.15.3": - version "7.15.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.15.3.tgz#94c81a6e2fc230bcce6ef537ac96a1e4d2b3afaf" - integrity sha512-nBAzfZwZb4DkaGtOes1Up1nOAp9TDRRFw4XBzBBSG9QK7KVFmYzgj9o9sbPv7TX5ofL4Auq4wZnxCoPnI/lz2Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-classes@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-classes/-/plugin-transform-classes-7.15.4.tgz#50aee17aaf7f332ae44e3bce4c2e10534d5d3bf1" - integrity sha512-Yjvhex8GzBmmPQUvpXRPWQ9WnxXgAFuZSrqOK/eJlOGIXwvv8H3UEdUigl1gb/bnjTrln+e8bkZUYCBt/xYlBg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" - integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-destructuring@^7.14.7": - version "7.14.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" - integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" - integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-duplicate-keys@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954" - integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-exponentiation-operator@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" - integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-for-of@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.15.4.tgz#25c62cce2718cfb29715f416e75d5263fb36a8c2" - integrity sha512-DRTY9fA751AFBDh2oxydvVm4SYevs5ILTWLs6xKXps4Re/KG5nfUkr+TdHCrRWB8C69TlzVgA9b3RmGWmgN9LA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-function-name@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" - integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-literals@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" - integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-member-expression-literals@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" - integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-modules-amd@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7" - integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz#8201101240eabb5a76c08ef61b2954f767b6b4c1" - integrity sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA== - dependencies: - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.15.4" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.15.4.tgz#b42890c7349a78c827719f1d2d0cd38c7d268132" - integrity sha512-fJUnlQrl/mezMneR72CKCgtOoahqGJNVKpompKwzv3BrEXdlPspTcyxrZ1XmDTIr9PpULrgEQo3qNKp6dW7ssw== - dependencies: - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.9" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0" - integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.14.9": - version "7.14.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.9.tgz#c68f5c5d12d2ebaba3762e57c2c4f6347a46e7b2" - integrity sha512-l666wCVYO75mlAtGFfyFwnWmIXQm3kSH0C3IRnJqWcZbWkoihyAdDhFm2ZWaxWTqvBvhVFfJjMRQ0ez4oN1yYA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - -"@babel/plugin-transform-new-target@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8" - integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-object-super@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" - integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - -"@babel/plugin-transform-parameters@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.15.4.tgz#5f2285cc3160bf48c8502432716b48504d29ed62" - integrity sha512-9WB/GUTO6lvJU3XQsSr6J/WKvBC2hcs4Pew8YxZagi6GkTdniyqp8On5kqdK8MN0LMeu0mGbhPN+O049NV/9FQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" - integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-regenerator@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304" - integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-shorthand-properties@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" - integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-spread@^7.14.6": - version "7.14.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - -"@babel/plugin-transform-sticky-regex@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" - integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-template-literals@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" - integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-typeof-symbol@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4" - integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-escapes@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" - integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-regex@^7.14.5": - version "7.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" - integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/preset-env@^7.12.1": - version "7.15.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/preset-env/-/preset-env-7.15.6.tgz#0f3898db9d63d320f21b17380d8462779de57659" - integrity sha512-L+6jcGn7EWu7zqaO2uoTDjjMBW+88FXzV8KvrBl2z6MtRNxlsmUNRlZPaNNPUTgqhyC5DHNFk/2Jmra+ublZWw== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.15.4" - "@babel/plugin-proposal-async-generator-functions" "^7.15.4" - "@babel/plugin-proposal-class-properties" "^7.14.5" - "@babel/plugin-proposal-class-static-block" "^7.15.4" - "@babel/plugin-proposal-dynamic-import" "^7.14.5" - "@babel/plugin-proposal-export-namespace-from" "^7.14.5" - "@babel/plugin-proposal-json-strings" "^7.14.5" - "@babel/plugin-proposal-logical-assignment-operators" "^7.14.5" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" - "@babel/plugin-proposal-numeric-separator" "^7.14.5" - "@babel/plugin-proposal-object-rest-spread" "^7.15.6" - "@babel/plugin-proposal-optional-catch-binding" "^7.14.5" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - "@babel/plugin-proposal-private-methods" "^7.14.5" - "@babel/plugin-proposal-private-property-in-object" "^7.15.4" - "@babel/plugin-proposal-unicode-property-regex" "^7.14.5" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.14.5" - "@babel/plugin-transform-async-to-generator" "^7.14.5" - "@babel/plugin-transform-block-scoped-functions" "^7.14.5" - "@babel/plugin-transform-block-scoping" "^7.15.3" - "@babel/plugin-transform-classes" "^7.15.4" - "@babel/plugin-transform-computed-properties" "^7.14.5" - "@babel/plugin-transform-destructuring" "^7.14.7" - "@babel/plugin-transform-dotall-regex" "^7.14.5" - "@babel/plugin-transform-duplicate-keys" "^7.14.5" - "@babel/plugin-transform-exponentiation-operator" "^7.14.5" - "@babel/plugin-transform-for-of" "^7.15.4" - "@babel/plugin-transform-function-name" "^7.14.5" - "@babel/plugin-transform-literals" "^7.14.5" - "@babel/plugin-transform-member-expression-literals" "^7.14.5" - "@babel/plugin-transform-modules-amd" "^7.14.5" - "@babel/plugin-transform-modules-commonjs" "^7.15.4" - "@babel/plugin-transform-modules-systemjs" "^7.15.4" - "@babel/plugin-transform-modules-umd" "^7.14.5" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.9" - "@babel/plugin-transform-new-target" "^7.14.5" - "@babel/plugin-transform-object-super" "^7.14.5" - "@babel/plugin-transform-parameters" "^7.15.4" - "@babel/plugin-transform-property-literals" "^7.14.5" - "@babel/plugin-transform-regenerator" "^7.14.5" - "@babel/plugin-transform-reserved-words" "^7.14.5" - "@babel/plugin-transform-shorthand-properties" "^7.14.5" - "@babel/plugin-transform-spread" "^7.14.6" - "@babel/plugin-transform-sticky-regex" "^7.14.5" - "@babel/plugin-transform-template-literals" "^7.14.5" - "@babel/plugin-transform-typeof-symbol" "^7.14.5" - "@babel/plugin-transform-unicode-escapes" "^7.14.5" - "@babel/plugin-transform-unicode-regex" "^7.14.5" - "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.15.6" - babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" - babel-plugin-polyfill-regenerator "^0.2.2" - core-js-compat "^3.16.0" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.4": - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" - integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/runtime-corejs3@^7.9.2": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz#403139af262b9a6e8f9ba04a6fdcebf8de692bf1" - integrity sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg== - dependencies: - core-js-pure "^3.16.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.8.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" - integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.15.4", "@babel/template@^7.3.3": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" - integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.4": - version "7.15.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.15.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" - integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== - dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - -"@google-cloud/common@^3.7.0": - version "3.7.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/common/-/common-3.7.2.tgz#2f1feef9aaeb4f392d627ceab06e7eaa03329f58" - integrity sha512-5Q9f74IbZaY6xAwJSNFy5SrGwbm1j7mpv+6A/r+K2dymjsXBH5UauB0tziaMwWoVVaMq1IQnZF9lgtfqqvxcUg== - dependencies: - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - arrify "^2.0.1" - duplexify "^4.1.1" - ent "^2.2.0" - extend "^3.0.2" - google-auth-library "^7.0.2" - retry-request "^4.2.2" - teeny-request "^7.0.0" - -"@google-cloud/monitoring@^2.1.0": - version "2.3.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/monitoring/-/monitoring-2.3.5.tgz#3f2d0b23402d51cca338e5700244cfda820e55fe" - integrity sha512-91+gobJMXEibKxnPOY3Q+ccFifQyRUdFpQ8uQ8acqWesxVj9ZH1VRm3ZpoleYWbbMe32cymhpGRamZ6t31veRQ== - dependencies: - google-gax "^2.24.1" - -"@google-cloud/paginator@^3.0.0": - version "3.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/paginator/-/paginator-3.0.6.tgz#02a59dccd348d515069779a4f77a4a4fd15594da" - integrity sha512-XCTm/GfQIlc1ZxpNtTSs/mnZxC2cePNhxU3X8EzHXKIJ2JFncmJj2Fcd2IP+gbmZaSZnY0juFxbUCkIeuu/2eQ== - dependencies: - arrify "^2.0.0" - extend "^3.0.2" - -"@google-cloud/projectify@^2.0.0": - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/projectify/-/projectify-2.1.1.tgz#ae6af4fee02d78d044ae434699a630f8df0084ef" - integrity sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ== - -"@google-cloud/promisify@^2.0.0": - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/promisify/-/promisify-2.0.4.tgz#9d8705ecb2baa41b6b2673f3a8e9b7b7e1abc52a" - integrity sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA== - -"@google-cloud/storage@^5.3.0": - version "5.14.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@google-cloud/storage/-/storage-5.14.3.tgz#7664d4d942dd3c024d7f0fbadc7a4c2aac4ca868" - integrity sha512-XwB+W/WcQMPQ8dH6i/d4hGnHsmb03k5IDtCG9HwwHszLWWCvzK7qX2G0fo+9nM4Ph8XIQrPNeHpRX4CwoQ3r2A== - dependencies: - "@google-cloud/common" "^3.7.0" - "@google-cloud/paginator" "^3.0.0" - "@google-cloud/promisify" "^2.0.0" - arrify "^2.0.0" - async-retry "^1.3.1" - compressible "^2.0.12" - date-and-time "^2.0.0" - duplexify "^4.0.0" - extend "^3.0.2" - gcs-resumable-upload "^3.3.0" - get-stream "^6.0.0" - hash-stream-validation "^0.2.2" - mime "^2.2.0" - mime-types "^2.0.8" - p-limit "^3.0.1" - pumpify "^2.0.0" - snakeize "^0.1.0" - stream-events "^1.0.1" - xdg-basedir "^4.0.0" - -"@grpc/grpc-js@~1.3.0": - version "1.3.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@grpc/grpc-js/-/grpc-js-1.3.7.tgz#58b687aff93b743aafde237fd2ee9a3259d7f2d8" - integrity sha512-CKQVuwuSPh40tgOkR7c0ZisxYRiN05PcKPW72mQL5y++qd7CwBRoaJZvU5xfXnCJDFBmS3qZGQ71Frx6Ofo2XA== - dependencies: - "@types/node" ">=12.12.47" - -"@grpc/proto-loader@^0.6.1": - version "0.6.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@grpc/proto-loader/-/proto-loader-0.6.5.tgz#f23c7cb3e7076a8702f40c2b6678f06fb9950a55" - integrity sha512-GZdzyVQI1Bln/kCzIYgTKu+rQJ5dno0gVrfmLe4jqQu7T2e7svSwJzpCBqVU5hhBSJP3peuPjOMWsj5GR61YmQ== - dependencies: - "@types/long" "^4.0.1" - lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.10.0" - yargs "^16.1.1" - -"@hubspot/api-client@^4.1.0": - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@hubspot/api-client/-/api-client-4.1.0.tgz#270aa7abbaa914beeb2edeab838dd506116f60b7" - integrity sha512-21JXLGk87j4lL1BlAJ9jRsl0PexmpEexhWhwSuFj8E82kHmvkO/Lz4QO+gxEltghfMlikVBIR0Z/pjnh6QNhkw== - dependencies: - bluebird "^3.7.2" - bottleneck "^2.19.5" - lodash "^4.17.21" - request "^2.88.0" - -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" - slash "^3.0.0" - -"@jest/core@^26.6.3": - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== - dependencies: - "@jest/console" "^26.6.2" - "@jest/reporters" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^26.6.2" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-resolve-dependencies "^26.6.3" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - jest-watcher "^26.6.2" - micromatch "^4.0.2" - p-each-series "^2.1.0" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== - dependencies: - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== - dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" - "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" - -"@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" - -"@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.4" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^26.6.2" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" - -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.4" - source-map "^0.6.0" - -"@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== - dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== - dependencies: - "@jest/test-result" "^26.6.2" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - -"@jest/transform@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" - integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^26.6.2" - babel-plugin-istanbul "^6.0.0" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-regex-util "^26.0.0" - jest-util "^26.6.2" - micromatch "^4.0.2" - pirates "^4.0.1" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - -"@koa/cors@^3.1.0": - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@koa/cors/-/cors-3.1.0.tgz#618bb073438cfdbd3ebd0e648a76e33b84f3a3b2" - integrity sha512-7ulRC1da/rBa6kj6P4g2aJfnET3z8Uf3SWu60cjbtxTA5g8lxRdX/Bd2P92EagGwwAhANeNw8T8if99rJliR6Q== - dependencies: - vary "^1.1.2" - -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" - integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== - dependencies: - detect-libc "^1.0.3" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.1" - nopt "^5.0.0" - npmlog "^4.1.2" - rimraf "^3.0.2" - semver "^7.3.4" - tar "^6.1.0" - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@panva/asn1.js@^1.0.0": - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" - integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= - -"@sendgrid/client@^7.4.7": - version "7.4.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sendgrid/client/-/client-7.4.7.tgz#052d7ea99a19ae880d7fb0e396337f0ab59c7e2a" - integrity sha512-Zh3H/nCyI2+MaAZW9KJpyk6JXfxE27W+mR636Hju/GX95SR7+uymeuLLJ3GtLvtapLTl8l6/0YoHjzlg65xtqg== - dependencies: - "@sendgrid/helpers" "^7.4.6" - axios "^0.21.4" - -"@sendgrid/helpers@^7.4.6": - version "7.4.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sendgrid/helpers/-/helpers-7.4.6.tgz#0fed40aeb9738a0d698ec2ce311c73f7460cfd72" - integrity sha512-Vvt4d60fkU/DPSwMyxXtlnbw4/B+5Y9eeYnygTxhmw8TNzUhdPphr7SaRSperWJ8P1VeQZzobvQNyMj5E7A3UA== - dependencies: - deepmerge "^4.2.2" - -"@sendgrid/mail@^7.1.0": - version "7.4.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sendgrid/mail/-/mail-7.4.7.tgz#cc8f77ec27443e02e7be1df01bf0899434155804" - integrity sha512-lGfXJBEx7PMQje/NsVsebF6MdP2ptHWjmuI4YANjReAQlIGq3Cqm4JLP5Fb4n5Bbr1LXLCM7R0gJo+/PT6ENKw== - dependencies: - "@sendgrid/client" "^7.4.7" - "@sendgrid/helpers" "^7.4.6" - -"@sentry/core@6.13.2": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/core/-/core-6.13.2.tgz#2ce164f81667aa89cd116f807d772b4718434583" - integrity sha512-snXNNFLwlS7yYxKTX4DBXebvJK+6ikBWN6noQ1CHowvM3ReFBlrdrs0Z0SsSFEzXm2S4q7f6HHbm66GSQZ/8FQ== - dependencies: - "@sentry/hub" "6.13.2" - "@sentry/minimal" "6.13.2" - "@sentry/types" "6.13.2" - "@sentry/utils" "6.13.2" - tslib "^1.9.3" - -"@sentry/hub@6.13.2": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/hub/-/hub-6.13.2.tgz#ebc66fd55c96c7686a53ffd3521b6a63f883bb79" - integrity sha512-sppSuJdNMiMC/vFm/dQowCBh11uTrmvks00fc190YWgxHshodJwXMdpc+pN61VSOmy2QA4MbQ5aMAgHzPzel3A== - dependencies: - "@sentry/types" "6.13.2" - "@sentry/utils" "6.13.2" - tslib "^1.9.3" - -"@sentry/minimal@6.13.2": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/minimal/-/minimal-6.13.2.tgz#de3ecc62b9463bf56ccdbcf4c75f7ea1aeeebc11" - integrity sha512-6iJfEvHzzpGBHDfLxSHcGObh73XU1OSQKWjuhDOe7UQDyI4BQmTfcXAC+Fr8sm8C/tIsmpVi/XJhs8cubFdSMw== - dependencies: - "@sentry/hub" "6.13.2" - "@sentry/types" "6.13.2" - tslib "^1.9.3" - -"@sentry/node@^6.4.1": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/node/-/node-6.13.2.tgz#6f5ee51eacad19b59e6ffb70b2d0e14396fd6233" - integrity sha512-0Vw22amG143MTiNaSny66YGU3+uW7HxyGI9TLGE7aJY1nNmC0DE+OgqQYGBRCrrPu+VFXRDxrOg9b15A1gKqjA== - dependencies: - "@sentry/core" "6.13.2" - "@sentry/hub" "6.13.2" - "@sentry/tracing" "6.13.2" - "@sentry/types" "6.13.2" - "@sentry/utils" "6.13.2" - cookie "^0.4.1" - https-proxy-agent "^5.0.0" - lru_map "^0.3.3" - tslib "^1.9.3" - -"@sentry/tracing@6.13.2", "@sentry/tracing@^6.4.1": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/tracing/-/tracing-6.13.2.tgz#512389ba459f48ae75e14f1528ab062dc46e4956" - integrity sha512-bHJz+C/nd6biWTNcYAu91JeRilsvVgaye4POkdzWSmD0XoLWHVMrpCQobGpXe7onkp2noU3YQjhqgtBqPHtnpw== - dependencies: - "@sentry/hub" "6.13.2" - "@sentry/minimal" "6.13.2" - "@sentry/types" "6.13.2" - "@sentry/utils" "6.13.2" - tslib "^1.9.3" - -"@sentry/types@6.13.2": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/types/-/types-6.13.2.tgz#8388d5b92ea8608936e7aae842801dc90e0184e6" - integrity sha512-6WjGj/VjjN8LZDtqJH5ikeB1o39rO1gYS6anBxiS3d0sXNBb3Ux0pNNDFoBxQpOhmdDHXYS57MEptX9EV82gmg== - -"@sentry/utils@6.13.2": - version "6.13.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sentry/utils/-/utils-6.13.2.tgz#fb8010e7b67cc8c084d8067d64ef25289269cda5" - integrity sha512-foF4PbxqPMWNbuqdXkdoOmKm3quu3PP7Q7j/0pXkri4DtCuvF/lKY92mbY0V9rHS/phCoj+3/Se5JvM2ymh2/w== - dependencies: - "@sentry/types" "6.13.2" - tslib "^1.9.3" - -"@shelf/jest-mongodb@^1.2.3": - version "1.3.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@shelf/jest-mongodb/-/jest-mongodb-1.3.4.tgz#200bac386cf513bed2d41952b1857689f0b88f31" - integrity sha512-PQe/5jN8wHr30d8422+2CV+XzbJTCFLGxzb0OrwbxrRiNdZA+FFXOqVak1vd3dqk4qogmmqEVQFkwQ4PNHzNgA== - dependencies: - debug "4.3.2" - mongodb-memory-server "6.9.6" - uuid "8.3.2" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== - dependencies: - "@sinonjs/commons" "^1.7.0" - -"@sinonjs/samsam@^5.3.1": - version "5.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" - integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== - dependencies: - "@sinonjs/commons" "^1.6.0" - lodash.get "^4.4.2" - type-detect "^4.0.8" - -"@sinonjs/text-encoding@^0.7.1": - version "0.7.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" - integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@tootallnate/once@1": - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - -"@types/accepts@*": - version "1.3.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" - integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== - dependencies: - "@types/node" "*" - -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.16" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/babel__core/-/babel__core-7.1.16.tgz#bc12c74b7d65e82d29876b5d0baf5c625ac58702" - integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" - integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" - integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== - dependencies: - "@babel/types" "^7.3.0" - -"@types/body-parser@*": - version "1.19.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" - integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/bson@*": - version "4.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337" - integrity sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg== - dependencies: - bson "*" - -"@types/bson@1.x || 4.0.x": - version "4.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/bson/-/bson-4.0.5.tgz#9e0e1d1a6f8866483f96868a9b33bc804926b1fc" - integrity sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg== - dependencies: - "@types/node" "*" - -"@types/connect@*": - version "3.4.35" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/content-disposition@*": - version "0.5.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" - integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== - -"@types/cookies@*": - version "0.7.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" - integrity sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA== - dependencies: - "@types/connect" "*" - "@types/express" "*" - "@types/keygrip" "*" - "@types/node" "*" - -"@types/express-serve-static-core@^4.17.18": - version "4.17.24" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" - integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express@*": - version "4.17.13" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/fined@*": - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/fined/-/fined-1.1.3.tgz#83f03e8f0a8d3673dfcafb18fce3571f6250e1bc" - integrity sha512-CWYnSRnun3CGbt6taXeVo2lCbuaj4mchVJ4UF/BdU5TSuIn3AmS13pGMwCsBUoehGbhZrBrpNJZSZI5EVilXww== - -"@types/glob@^7.1.1": - version "7.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" - integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - -"@types/http-assert@*": - version "1.5.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" - integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== - -"@types/http-errors@*": - version "1.8.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67" - integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q== - -"@types/inquirer@^6.5.0": - version "6.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be" - integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw== - dependencies: - "@types/through" "*" - rxjs "^6.4.0" - -"@types/interpret@*": - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/interpret/-/interpret-1.1.1.tgz#b1bf85b0420e2414b989ce237658ad20dc03719b" - integrity sha512-HZ4d0m2Ebl8DmrOdYZHgYyipj/8Ftq1/ssB/oQR7fqfUrwtTP7IW3BDi2V445nhPBLzZjEkApaPVp83moSCXlA== - dependencies: - "@types/node" "*" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/keygrip@*": - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" - integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== - -"@types/koa-compose@*": - version "3.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" - integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== - dependencies: - "@types/koa" "*" - -"@types/koa@*", "@types/koa@^2.11.4": - version "2.13.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" - integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== - dependencies: - "@types/accepts" "*" - "@types/content-disposition" "*" - "@types/cookies" "*" - "@types/http-assert" "*" - "@types/http-errors" "*" - "@types/keygrip" "*" - "@types/koa-compose" "*" - "@types/node" "*" - -"@types/liftoff@^2.5.0": - version "2.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/liftoff/-/liftoff-2.5.1.tgz#2eb4c1f86e9d5ee85571e56db0084b26af129ced" - integrity sha512-nB3R6Q9CZcM07JgiTK6ibxqrG1reiHE+UX7em/W1DKwVBxDlfKWOefQjk4jubY5xX+GDxVsWR2KD1SenPby8ow== - dependencies: - "@types/fined" "*" - "@types/interpret" "*" - "@types/node" "*" - -"@types/long@^4.0.0", "@types/long@^4.0.1": - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" - integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== - -"@types/mime@^1": - version "1.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== - -"@types/minimatch@*": - version "3.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== - -"@types/mongodb@^3.5.27": - version "3.6.20" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/mongodb/-/mongodb-3.6.20.tgz#b7c5c580644f6364002b649af1c06c3c0454e1d2" - integrity sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ== - dependencies: - "@types/bson" "*" - "@types/node" "*" - -"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "16.10.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/node/-/node-16.10.1.tgz#f3647623199ca920960006b3dccf633ea905f243" - integrity sha512-4/Z9DMPKFexZj/Gn3LylFgamNKHm4K3QDi0gz9B26Uk0c8izYf97B5fxfpspMNkWlFupblKM/nV8+NA9Ffvr+w== - -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - -"@types/prettier@^2.0.0": - version "2.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/prettier/-/prettier-2.4.0.tgz#900b13362610ccd3570fb6eefb911a6732973d00" - integrity sha512-WHRsy5nMpjXfU9B0LqOqPT06EI2+8Xv5NERy0pLxJLbU98q7uhcGogQzfX+rXpU7S5mgHsLxHrLCufZcV/P8TQ== - -"@types/qs@*": - version "6.9.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/serve-static@*": - version "1.13.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/through@*": - version "0.0.30" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== - dependencies: - "@types/node" "*" - -"@types/tmp@^0.2.0": - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/tmp/-/tmp-0.2.1.tgz#83ecf4ec22a8c218c71db25f316619fe5b986011" - integrity sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg== - -"@types/yargs-parser@*": - version "20.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== - -"@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== - dependencies: - "@types/yargs-parser" "*" - -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - -abbrev@1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -accepts@^1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.3.1: - version "5.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.2.4: - version "8.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== - -agent-base@6: - version "6.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-formats@^2.0.2: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: - version "6.12.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.1.0: - version "8.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" - integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== - dependencies: - string-width "^3.0.0" - -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-font@0.0.2: - version "0.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-font/-/ansi-font-0.0.2.tgz#890301bd5841462fd39c0b7709afd1f525143331" - integrity sha1-iQMBvVhBRi/TnAt3Ca/R9SUUMzE= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0, ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -append-field@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= - -aproba@^1.0.3: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" - integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-each@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-union@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array-uniq@1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-uniq/-/array-uniq-1.0.2.tgz#5fcc373920775723cfd64d65c64bef53bf9eba6d" - integrity sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -arrify@^2.0.0, arrify@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" - integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== - -asn1@~0.2.3: - version "0.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async-retry@^1.3.1: - version "1.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" - integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== - dependencies: - retry "0.13.1" - -async@0.9.x: - version "0.9.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - -async@^3.2.0: - version "3.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" - integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.2: - version "2.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -await-to-js@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/await-to-js/-/await-to-js-2.1.1.tgz#c2093cd5a386f2bb945d79b292817bbc3f41b31b" - integrity sha512-CHBC6gQGCIzjZ09tJ+XmpQoZOn4GdWePB4qUweCaKNJ0D3f115YdhmYVTZ4rMVpiJ3cFzZcTYK1VMYEICV4YXw== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -axios-retry@^3.1.9: - version "3.1.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8" - integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA== - dependencies: - is-retry-allowed "^1.1.0" - -axios@0.21.1: - version "0.21.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== - dependencies: - follow-redirects "^1.10.0" - -axios@^0.21.4: - version "0.21.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -babel-jest@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" - integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== - dependencies: - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__core" "^7.1.7" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.6.2" - chalk "^4.0.0" - graceful-fs "^4.2.4" - slash "^3.0.0" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" - integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-polyfill-corejs2@^0.2.2: - version "0.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" - integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== - dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.2" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz#2779846a16a1652244ae268b1e906ada107faf92" - integrity sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - core-js-compat "^3.16.2" - -babel-plugin-polyfill-regenerator@^0.2.2: - version "0.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" - integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" - integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== - dependencies: - babel-plugin-jest-hoist "^26.6.2" - babel-preset-current-node-syntax "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.0, base64-js@^1.3.1: - version "1.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64url@3.x.x, base64url@^3.0.1: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - -base@^0.11.1: - version "0.11.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -basic-auth@~2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -bcrypt@^5.0.0: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71" - integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^3.1.0" - -bignumber.js@^9.0.0: - version "9.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" - integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bl@^2.2.1: - version "2.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" - integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - -bl@^4.0.3: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@3.5.1: - version "3.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== - -bluebird@^3.3.3, bluebird@^3.4.0, bluebird@^3.7.2: - version "3.7.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -bottleneck@^2.19.5: - version "2.19.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" - integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== - -boxen@^5.0.0: - version "5.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1: - version "2.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1, braces@~3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserslist@^4.16.6, browserslist@^4.17.1: - version "4.17.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/browserslist/-/browserslist-4.17.1.tgz#a98d104f54af441290b7d592626dd541fa642eb9" - integrity sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ== - dependencies: - caniuse-lite "^1.0.30001259" - electron-to-chromium "^1.3.846" - escalade "^3.1.1" - nanocolors "^0.1.5" - node-releases "^1.1.76" - -bser@2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -bson@*: - version "4.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bson/-/bson-4.5.2.tgz#567b4ee94372d5284a4d6c47fb6e1cc711ae76ba" - integrity sha512-8CEMJpwc7qlQtrn2rney38jQSEeMar847lz0LyitwRmVknAW8iHXrzW4fTjHfyWm0E3sukyD/zppdH+QU1QefA== - dependencies: - buffer "^5.6.0" - -bson@^1.1.4: - version "1.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a" - integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg== - -btoa@^1.2.1: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" - integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@^5.5.0, buffer@^5.6.0: - version "5.7.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -busboy@^0.2.11: - version "0.2.14" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" - integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= - dependencies: - dicer "0.2.5" - readable-stream "1.1.x" - -bytes@3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cache-content-type@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" - integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== - dependencies: - mime-types "^2.1.18" - ylru "^1.2.0" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -call-bind@^1.0.0: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camel-case@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0, camelcase@^6.2.0: - version "6.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -caniuse-lite@^1.0.30001259: - version "1.0.30001260" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz#e3be3f34ddad735ca4a2736fa9e768ef34316270" - integrity sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg== - dependencies: - nanocolors "^0.1.0" - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -change-case@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" - integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw== - dependencies: - camel-case "^3.0.0" - constant-case "^2.0.0" - dot-case "^2.1.0" - header-case "^1.0.0" - is-lower-case "^1.1.0" - is-upper-case "^1.1.0" - lower-case "^1.1.1" - lower-case-first "^1.0.0" - no-case "^2.3.2" - param-case "^2.1.0" - pascal-case "^2.0.0" - path-case "^2.1.0" - sentence-case "^2.1.0" - snake-case "^2.1.0" - swap-case "^1.1.0" - title-case "^2.1.0" - upper-case "^1.1.1" - upper-case-first "^1.1.0" - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -chardet@^0.7.0: - version "0.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chokidar@^3.2.2: - version "3.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chownr@^1.1.4: - version "1.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.0.0: - version "2.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" - integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== - -cli-width@^2.0.0: - version "2.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^3.2.0: - version "3.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -co@^4.6.0: - version "4.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colors@^1.1.2: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commondir@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -component-emitter@^1.2.0, component-emitter@^1.2.1: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compressible@^2.0.12: - version "2.0.18" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.5.2: - version "1.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -configstore@^5.0.0, configstore@^5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -constant-case@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" - integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= - dependencies: - snake-case "^2.1.0" - upper-case "^1.1.1" - -content-disposition@0.5.3, content-disposition@~0.5.2: - version "0.5.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@^1.0.4, content-type@~1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -cookie-parser@^1.4.5: - version "1.4.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookie-parser/-/cookie-parser-1.4.5.tgz#3e572d4b7c0c80f9c61daf604e4336831b5d1d49" - integrity sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw== - dependencies: - cookie "0.4.0" - cookie-signature "1.0.6" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -cookie@0.4.1, cookie@^0.4.1: - version "0.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== - -cookiejar@^2.1.0: - version "2.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" - integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== - -cookies@~0.8.0: - version "0.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" - integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== - dependencies: - depd "~2.0.0" - keygrip "~1.1.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-js-compat@^3.16.0, core-js-compat@^3.16.2: - version "3.18.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/core-js-compat/-/core-js-compat-3.18.1.tgz#01942a0877caf9c6e5007c027183cf0bdae6a191" - integrity sha512-XJMYx58zo4W0kLPmIingVZA10+7TuKrMLPt83+EzDmxFJQUMcTVVmQ+n5JP4r6Z14qSzhQBRi3NSWoeVyKKXUg== - dependencies: - browserslist "^4.17.1" - semver "7.0.0" - -core-js-pure@^3.16.0: - version "3.18.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/core-js-pure/-/core-js-pure-3.18.1.tgz#097d34d24484be45cea700a448d1e74622646c80" - integrity sha512-kmW/k8MaSuqpvA1xm2l3TVlBuvW+XBkcaOroFUpO3D4lsTGQWBTb/tBDCf/PNkkPLrwgrkQRIYNPB0CeqGJWGQ== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cors@^2.8.5: - version "2.8.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-js@^4.0.0: - version "4.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== - -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - -crypto@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" - integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== - -csrf@3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/csrf/-/csrf-3.1.0.tgz#ec75e9656d004d674b8ef5ba47b41fbfd6cb9c30" - integrity sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w== - dependencies: - rndm "1.2.0" - tsscmp "1.0.6" - uid-safe "2.1.5" - -cssom@^0.4.4: - version "0.4.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csurf@^1.11.0: - version "1.11.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/csurf/-/csurf-1.11.0.tgz#ab0c3c6634634192bd3d6f4b861be20800eeb61a" - integrity sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ== - dependencies: - cookie "0.4.0" - cookie-signature "1.0.6" - csrf "3.1.0" - http-errors "~1.7.3" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -data-urls@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -date-and-time@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/date-and-time/-/date-and-time-2.0.0.tgz#99f5fb6b6c7bcd4d1f6dcbeb37553dc0ff797b65" - integrity sha512-HJSzj25iPm8E01nt+rSmCIlwjsmjvKfUivG/kXBglpymcHF1FolWAqWwTEV4FvN1Lx5UjPf0J1W4H8yQsVBfFg== - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@4, debug@4.3.2, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2: - version "4.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@^3.1.0, debug@^3.2.6: - version "3.2.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.1.1, decamelize@^1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -defaults@^1.0.3: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -del@^5.1.0: - version "5.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" - integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== - dependencies: - globby "^10.0.1" - graceful-fs "^4.2.2" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.1" - p-map "^3.0.0" - rimraf "^3.0.0" - slash "^3.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -denque@^1.4.1: - version "1.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" - integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== - -depd@^2.0.0, depd@~2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@^1.0.4, destroy@~1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -dicer@0.2.5: - version "0.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" - integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= - dependencies: - readable-stream "1.1.x" - streamsearch "0.1.2" - -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== - -diff@^4.0.2: - version "4.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -discourse-sso@^1.0.3: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/discourse-sso/-/discourse-sso-1.0.5.tgz#b5e73267726f60516573864748c71684c1308bd7" - integrity sha512-s3ob/jd3MT0gjJjPvfTYujQlZ5+PSz7LwCQ/N2M+lkSP1FDLqhh3Rd2WlOnhINJviVViZ+qjMzUUfWR15uAoYA== - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -domexception@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - -dot-case@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" - integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= - dependencies: - no-case "^2.2.0" - -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -dotenv@^8.0.0, dotenv@^8.2.0: - version "8.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexify@^4.0.0, duplexify@^4.1.1: - version "4.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" - integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== - dependencies: - end-of-stream "^1.4.1" - inherits "^2.0.3" - readable-stream "^3.1.1" - stream-shift "^1.0.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: - version "1.0.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -ejs@^3.1.5: - version "3.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== - dependencies: - jake "^10.6.1" - -electron-to-chromium@^1.3.846: - version "1.3.850" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/electron-to-chromium/-/electron-to-chromium-1.3.850.tgz#c56c72abfeab051b4b328beb894461c5912d0456" - integrity sha512-ZzkDcdzePeF4dhoGZQT77V2CyJOpwfTZEOg4h0x6R/jQhGt/rIRpbRyVreWLtD7B/WsVxo91URm2WxMKR9JQZA== - -emittery@^0.7.1: - version "0.7.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -encodeurl@^1.0.2, encodeurl@~1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enquirer@^2.3.5: - version "2.3.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -ent@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - -error-ex@^1.2.0, error-ex@^1.3.1: - version "1.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-html@^1.0.3, escape-html@~1.0.3: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint@^7.20.0: - version "7.32.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -esm@^3.2.25: - version "3.2.25" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - -execa@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.0.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - -exit@^0.1.2: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -expect@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== - dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - -express-rate-limit@^5.1.3: - version "5.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/express-rate-limit/-/express-rate-limit-5.3.0.tgz#e7b9d3c2e09ece6e0406a869b2ce00d03fe48aea" - integrity sha512-qJhfEgCnmteSeZAeuOKQ2WEIFTX5ajrzE0xS6gCOBCoRQcU+xEzQmgYQQTpzCcqUAAzTEtu4YEih4pnLfvNtew== - -express-session@^1.17.1: - version "1.17.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd" - integrity sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ== - dependencies: - cookie "0.4.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~2.0.0" - on-headers "~1.0.2" - parseurl "~1.3.3" - safe-buffer "5.2.1" - uid-safe "~2.1.5" - -express-validator@^6.6.1: - version "6.12.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/express-validator/-/express-validator-6.12.2.tgz#0ee3e34338223ae6ed9a97532ff3687fd561db90" - integrity sha512-UMVck7ZWrKH7eX75CRYk/pAc9jxZk8Ddsdkukw1R7LTWuQNiDaooz6nVfIdg33qZUHCuv2m2o+RS4pTMaLjGmA== - dependencies: - lodash "^4.17.21" - validator "^13.5.2" - -express@^4.17.1: - version "4.17.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -faker@^5.3.1: - version "5.5.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" - integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.0.3: - version "3.2.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== - -fastq@^1.6.0: - version "1.13.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - -fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== - dependencies: - bser "2.1.1" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -figures@^1.3.5: - version "1.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^3.0.0: - version "3.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -filelist@^1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== - dependencies: - minimatch "^3.0.4" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-package-json@^1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083" - integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw== - -find-up@^1.0.0: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== - -follow-redirects@^1.10.0, follow-redirects@^1.14.0: - version "1.14.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" - integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@^2.3.1: - version "2.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -form-data@^3.0.0: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@~2.3.2: - version "2.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -formidable@^1.2.0: - version "1.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" - integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== - -forwarded@0.2.0: - version "0.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2, fresh@~0.5.2: - version "0.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^2.1.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -gauge@~2.7.3: - version "2.7.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gaxios@^4.0.0: - version "4.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gaxios/-/gaxios-4.3.2.tgz#845827c2dc25a0213c8ab4155c7a28910f5be83f" - integrity sha512-T+ap6GM6UZ0c4E6yb1y/hy2UB6hTrqhglp3XfmU9qbLCGRYhLVV5aRPpC4EmoG8N8zOnkYCgoBz+ScvGAARY6Q== - dependencies: - abort-controller "^3.0.0" - extend "^3.0.2" - https-proxy-agent "^5.0.0" - is-stream "^2.0.0" - node-fetch "^2.6.1" - -gcp-metadata@^4.2.0: - version "4.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9" - integrity sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A== - dependencies: - gaxios "^4.0.0" - json-bigint "^1.0.0" - -gcs-resumable-upload@^3.3.0: - version "3.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gcs-resumable-upload/-/gcs-resumable-upload-3.3.1.tgz#bb3b0d776ce64b7c40d81fffadac7d54d878a9f3" - integrity sha512-WyC0i4VkslIdrdmeM5PNuGzANALLXTG5RoHb08OE30gYT+FEvCDPiA8KOjV2s1wOu9ngEW4+IuzBjtP/ni7UdQ== - dependencies: - abort-controller "^3.0.0" - configstore "^5.0.0" - extend "^3.0.2" - gaxios "^4.0.0" - google-auth-library "^7.0.0" - pumpify "^2.0.0" - stream-events "^1.0.4" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-caller-file@^2.0.1, get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-port@^5.1.1: - version "5.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stream@^4.0.0, get-stream@^4.1.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: - version "7.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== - dependencies: - ini "2.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globals@^11.1.0: - version "11.12.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.6.0, globals@^13.9.0: - version "13.11.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" - integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== - dependencies: - type-fest "^0.20.2" - -globby@^10.0.1: - version "10.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - -google-auth-library@^6.0.0: - version "6.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" - integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== - dependencies: - arrify "^2.0.0" - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^4.0.0" - gcp-metadata "^4.2.0" - gtoken "^5.0.4" - jws "^4.0.0" - lru-cache "^6.0.0" - -google-auth-library@^7.0.0, google-auth-library@^7.0.2, google-auth-library@^7.6.1: - version "7.9.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/google-auth-library/-/google-auth-library-7.9.2.tgz#c11c1b4d4cc50e85ea7c8a01f6d4ec2fbed1aed1" - integrity sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g== - dependencies: - arrify "^2.0.0" - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^4.0.0" - gcp-metadata "^4.2.0" - gtoken "^5.0.4" - jws "^4.0.0" - lru-cache "^6.0.0" - -google-gax@^2.24.1: - version "2.25.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/google-gax/-/google-gax-2.25.4.tgz#9fe30cf7ccbcf41eb57f74d72b75c5c4d08b5003" - integrity sha512-+Jd0FFOWyb8ieX53e6Sl5OYvHXoA1sWKfQ24ykR502NKgBTvPAh/RFcITihGePBJZ1E8pfh4MKWU0Sf+f1CK+A== - dependencies: - "@grpc/grpc-js" "~1.3.0" - "@grpc/proto-loader" "^0.6.1" - "@types/long" "^4.0.0" - abort-controller "^3.0.0" - duplexify "^4.0.0" - fast-text-encoding "^1.0.3" - google-auth-library "^7.6.1" - is-stream-ended "^0.1.4" - node-fetch "^2.6.1" - object-hash "^2.1.1" - proto3-json-serializer "^0.1.1" - protobufjs "6.11.2" - retry-request "^4.0.0" - -google-p12-pem@^3.0.3: - version "3.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/google-p12-pem/-/google-p12-pem-3.1.2.tgz#c3d61c2da8e10843ff830fdb0d2059046238c1d4" - integrity sha512-tjf3IQIt7tWCDsa0ofDQ1qqSCNzahXDxdAGJDbruWqu3eCg5CKLYKN+hi0s6lfvzYZ1GDVr+oDF9OOWlDSdf0A== - dependencies: - node-forge "^0.10.0" - -googleapis-common@^4.4.0: - version "4.4.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/googleapis-common/-/googleapis-common-4.4.3.tgz#a2063adf17b14501a5f426b9cb0685496d835b7d" - integrity sha512-W46WKCk3QtlCCfmZyQIH5zxmDOyeV5Qj+qs7nr2ox08eRkEJMWp6iwv542R/PsokXaGUSrmif4vCC4+rGzRSsQ== - dependencies: - extend "^3.0.2" - gaxios "^4.0.0" - google-auth-library "^6.0.0" - qs "^6.7.0" - url-template "^2.0.8" - uuid "^8.0.0" - -googleapis@^55.0.0: - version "55.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/googleapis/-/googleapis-55.0.0.tgz#02c7062bc9e40d55e8dc6ebcedc8831ad5edd04a" - integrity sha512-bjeEjI2183eU0RsPIYsVOQJpjJvPprdCFe6AtjT32SBYX7GW2rZgRP9Qna8aCbDEutyJjP8uBscirkDDpWLhQw== - dependencies: - google-auth-library "^6.0.0" - googleapis-common "^4.4.0" - -got@^9.6.0: - version "9.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.2.2, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== - -growly@^1.3.0: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= - -gtoken@^5.0.4: - version "5.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/gtoken/-/gtoken-5.3.1.tgz#c1c2598a826f2b5df7c6bb53d7be6cf6d50c3c78" - integrity sha512-yqOREjzLHcbzz1UrQoxhBtpk8KjrVhuqPE7od1K2uhyxG2BHjKZetlbLw/SPZak/QqTIQW+addS+EcjqQsZbwQ== - dependencies: - gaxios "^4.0.0" - google-p12-pem "^3.0.3" - jws "^4.0.0" - -handlebars@^4.4.3: - version "4.7.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - -has@^1.0.3: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-stream-validation@^0.2.2: - version "0.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" - integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== - -header-case@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" - integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.3" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-assert@^1.3.0: - version "1.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" - integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== - dependencies: - deep-equal "~1.0.1" - http-errors "~1.8.0" - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-errors@1.7.2: - version "1.7.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@1.7.3, http-errors@~1.7.2, http-errors@~1.7.3: - version "1.7.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@^1.6.3, http-errors@~1.8.0: - version "1.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" - integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -iconv-lite@0.4.24, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= - -ignore@^4.0.6: - version "4.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.1: - version "5.1.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - -import-local@^3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.3: - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - -ini@^1.3.4, ini@~1.3.0: - version "1.3.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inquirer@^0.12.0: - version "0.12.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -inquirer@^7.1.0: - version "7.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -interpret@^1.2.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-glob/-/is-glob-4.0.2.tgz#859fc2e731e58c902f99fcabccb75a7dd07d29d8" - integrity sha512-ZZTOjRcDjuAAAv2cTBQP/lL59ZTArx77+7UzHdWW/XB1mrfp7DEaVpKmZ0XIzx+M7AxfhKcqV+nMetUQmFifwg== - dependencies: - is-extglob "^2.1.1" - -is-installed-globally@^0.4.0: - version "0.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - -is-lower-case@^1.1.0: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" - integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= - dependencies: - lower-case "^1.1.0" - -is-npm@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" - integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== - -is-number@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-inside@^3.0.1, is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - -is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - -is-stream-ended@^0.1.4: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" - integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-typedarray@^1.0.0, is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-upper-case@^1.1.0: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" - integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= - dependencies: - upper-case "^1.1.0" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - -isarray@0.0.1: - version "0.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isbinaryfile@^4.0.2: - version "4.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -istanbul-lib-coverage@^3.0.0: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz#e8900b3ed6069759229cf30f7067388d148aeb5e" - integrity sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ== - -istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jake@^10.6.1: - version "10.8.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" - integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== - dependencies: - async "0.9.x" - chalk "^2.4.2" - filelist "^1.0.1" - minimatch "^3.0.4" - -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== - dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" - -jest-cli@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== - dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" - prompts "^2.0.1" - yargs "^15.4.1" - -jest-config@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" - chalk "^4.0.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.4" - jest-environment-jsdom "^26.6.2" - jest-environment-node "^26.6.2" - jest-get-type "^26.3.0" - jest-jasmine2 "^26.6.3" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" - -jest-diff@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== - dependencies: - detect-newline "^3.0.0" - -jest-each@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== - dependencies: - "@jest/types" "^26.6.2" - chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" - -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" - -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== - dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - -jest-haste-map@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== - dependencies: - "@jest/types" "^26.6.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.1.2" - -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^26.6.2" - is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - throat "^5.0.0" - -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== - dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== - dependencies: - chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" - slash "^3.0.0" - stack-utils "^2.0.2" - -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== - -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== - dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" - -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== - dependencies: - "@jest/types" "^26.6.2" - chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" - slash "^3.0.0" - -jest-runner@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== - dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.7.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-docblock "^26.0.0" - jest-haste-map "^26.6.2" - jest-leak-detector "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - jest-runtime "^26.6.3" - jest-util "^26.6.2" - jest-worker "^26.6.2" - source-map-support "^0.5.6" - throat "^5.0.0" - -jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== - dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/globals" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - cjs-module-lexer "^0.6.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - slash "^3.0.0" - strip-bom "^4.0.0" - yargs "^15.4.1" - -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" - chalk "^4.0.0" - expect "^26.6.2" - graceful-fs "^4.2.4" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - jest-haste-map "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - natural-compare "^1.4.0" - pretty-format "^26.6.2" - semver "^7.3.2" - -jest-util@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== - dependencies: - "@jest/types" "^26.6.2" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" - -jest-validate@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== - dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" - chalk "^4.0.0" - jest-get-type "^26.3.0" - leven "^3.1.0" - pretty-format "^26.6.2" - -jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== - dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^26.6.2" - string-length "^4.0.1" - -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest@^26.6.3: - version "26.6.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" - integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== - dependencies: - "@jest/core" "^26.6.3" - import-local "^3.0.2" - jest-cli "^26.6.3" - -jose@^2.0.2, jose@^2.0.4: - version "2.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jose/-/jose-2.0.5.tgz#29746a18d9fff7dcf9d5d2a6f62cb0c7cd27abd3" - integrity sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA== - dependencies: - "@panva/asn1.js" "^1.0.0" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsdom@^16.4.0: - version "16.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@^3.0.1: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^2.1.2: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1: - version "8.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^5.6.0" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -just-extend@^4.0.2: - version "4.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" - integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== - -jwa@^1.4.1: - version "1.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jwa@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" - integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - -jws@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== - dependencies: - jwa "^2.0.0" - safe-buffer "^5.0.1" - -kareem@2.3.2: - version "2.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kareem/-/kareem-2.3.2.tgz#78c4508894985b8d38a0dc15e1a8e11078f2ca93" - integrity sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ== - -keygrip@^1.1.0, keygrip@~1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" - integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== - dependencies: - tsscmp "1.0.6" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -koa-compose@^4.1.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" - integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== - dependencies: - co "^4.6.0" - koa-compose "^4.1.0" - -koa@^2.13.0: - version "2.13.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/koa/-/koa-2.13.3.tgz#a62641ba753ec54bee2c6da1a4f294c5fac35407" - integrity sha512-XhXIoR+ylAwqG3HhXwnMPQAM/4xfywz52OvxZNmxmTWGGHsvmBv4NSIhURha6yMuvEex1WdtplUTHnxnKpQiGw== - dependencies: - accepts "^1.3.5" - cache-content-type "^1.0.0" - content-disposition "~0.5.2" - content-type "^1.0.4" - cookies "~0.8.0" - debug "^4.3.2" - delegates "^1.0.0" - depd "^2.0.0" - destroy "^1.0.4" - encodeurl "^1.0.2" - escape-html "^1.0.3" - fresh "~0.5.2" - http-assert "^1.3.0" - http-errors "^1.6.3" - is-generator-function "^1.0.7" - koa-compose "^4.1.0" - koa-convert "^2.0.0" - on-finished "^2.3.0" - only "~0.0.2" - parseurl "^1.3.2" - statuses "^1.5.0" - type-is "^1.6.16" - vary "^1.1.2" - -latest-version@^5.1.0: - version "5.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -leven@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -levn@~0.3.0: - version "0.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -liftoff@^2.5.0: - version "2.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" - integrity sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew= - dependencies: - extend "^3.0.0" - findup-sync "^2.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lockfile@^1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" - integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== - dependencies: - signal-exit "^3.0.2" - -lodash.assign@^4.0.3, lodash.assign@^4.0.6: - version "4.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= - -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.3.0, lodash@^4.7.0: - version "4.17.21" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -long@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -lower-case-first@^1.0.0: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" - integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= - dependencies: - lower-case "^1.1.2" - -lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: - version "1.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru_map@^0.3.3: - version "0.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" - integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= - -mailchimp-api-v3@^1.15.0: - version "1.15.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mailchimp-api-v3/-/mailchimp-api-v3-1.15.0.tgz#bcc36dcb400f6e3ae7524526824134c134bf4f93" - integrity sha512-9TxCFG+VRpl14HOHgABHYmC5GvpCY7LYqyTefOXd4GtI07oXCiJ7W5fEvk3SJKBctlbjhKbzjB5qOZMQpacEUQ== - dependencies: - bluebird "^3.4.0" - lodash "^4.17.14" - request "^2.88.0" - tar "^4.0.2" - -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -makeerror@1.0.x: - version "1.0.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= - dependencies: - tmpl "1.0.x" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -md5-file@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/md5-file/-/md5-file-5.0.0.tgz#e519f631feca9c39e7f9ea1780b63c4745012e20" - integrity sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -memory-pager@^1.0.2: - version "1.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" - integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.2.3, merge2@^1.3.0: - version "1.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.0.4, micromatch@^3.1.4: - version "3.1.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - -migrate-mongoose@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/migrate-mongoose/-/migrate-mongoose-4.0.0.tgz#a78a1eaff2b65c0483b2b9a817c36683adf220ac" - integrity sha512-Zf4Jk+CvBZUrZx4q/vvYr2pRGYAo7RO4BJx/3aTAR9VhNa34/iV0Rhqj87Tflk0n14SgwZpqvixyJzEpmSAikg== - dependencies: - bluebird "^3.3.3" - colors "^1.1.2" - dotenv "^8.0.0" - inquirer "^0.12.0" - mkdirp "^0.5.1" - mongoose "^5.6.3" - yargs "^4.8.1" - -mime-db@1.49.0: - version "1.49.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" - integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== - -"mime-db@>= 1.43.0 < 2": - version "1.50.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" - integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== - -mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.32" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" - integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== - dependencies: - mime-db "1.49.0" - -mime@1.6.0, mime@^1.4.1: - version "1.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.2.0: - version "2.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minipass@^3.0.0: - version "3.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732" - integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw== - dependencies: - yallist "^4.0.0" - -minizlib@^1.3.3: - version "1.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5: - version "0.5.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -moment@^2.27.0: - version "2.29.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== - -mongodb-memory-server-core@6.9.2: - version "6.9.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb-memory-server-core/-/mongodb-memory-server-core-6.9.2.tgz#a064602e85c065c63776cef20ec7311d2b2da206" - integrity sha512-0naMEESKsJNBg4/djN9qc+Argmg5UElJ/EFP9M4opTH//GZ1Rn6SI5S43NFHJrizOPGojAAp21gn7rNOru7Ypw== - dependencies: - "@types/tmp" "^0.2.0" - camelcase "^6.0.0" - cross-spawn "^7.0.3" - debug "^4.2.0" - find-cache-dir "^3.3.1" - find-package-json "^1.2.0" - get-port "^5.1.1" - https-proxy-agent "^5.0.0" - lockfile "^1.0.4" - md5-file "^5.0.0" - mkdirp "^1.0.4" - semver "^7.3.2" - tar-stream "^2.1.4" - tmp "^0.2.1" - uuid "8.3.0" - yauzl "^2.10.0" - optionalDependencies: - mongodb "3.6.2" - -mongodb-memory-server-core@6.9.6: - version "6.9.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb-memory-server-core/-/mongodb-memory-server-core-6.9.6.tgz#90ef0562bea675ef68bd687533792da02bcc81f3" - integrity sha512-ZcXHTI2TccH3L5N9JyAMGm8bbAsfLn8SUWOeYGHx/vDx7vu4qshyaNXTIxeHjpUQA29N+Z1LtTXA6vXjl1eg6w== - dependencies: - "@types/tmp" "^0.2.0" - camelcase "^6.0.0" - cross-spawn "^7.0.3" - debug "^4.2.0" - find-cache-dir "^3.3.1" - find-package-json "^1.2.0" - get-port "^5.1.1" - https-proxy-agent "^5.0.0" - lockfile "^1.0.4" - md5-file "^5.0.0" - mkdirp "^1.0.4" - semver "^7.3.2" - tar-stream "^2.1.4" - tmp "^0.2.1" - uuid "^8.3.0" - yauzl "^2.10.0" - optionalDependencies: - mongodb "^3.6.2" - -mongodb-memory-server@6.9.2: - version "6.9.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb-memory-server/-/mongodb-memory-server-6.9.2.tgz#75880bf5f485deceba2d7df20659b2796ff703cf" - integrity sha512-+8axA5PlO+C3H+kgsxt6+6edcKAaY56YjYt+MWj9t1ZiKsEr+7SPsQfJcEoX+Kiz802jt1BOOIbYQVLX+08Hag== - dependencies: - mongodb-memory-server-core "6.9.2" - -mongodb-memory-server@6.9.6: - version "6.9.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb-memory-server/-/mongodb-memory-server-6.9.6.tgz#ced1a100f58363317a562efaf8821726c433cfd2" - integrity sha512-BjGPPh5f61lMueG7px9DneBIrRR/GoWUHDvLWVAXhQhKVcwMMXxgeEba6zdDolZHfYAu6aYGPzhOuYKIKPgpBQ== - dependencies: - mongodb-memory-server-core "6.9.6" - -mongodb@3.6.11: - version "3.6.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb/-/mongodb-3.6.11.tgz#8a59a0491a92b00a8c925f72ed9d9a5b054aebb2" - integrity sha512-4Y4lTFHDHZZdgMaHmojtNAlqkvddX2QQBEN0K//GzxhGwlI9tZ9R0vhbjr1Decw+TF7qK0ZLjQT292XgHRRQgw== - dependencies: - bl "^2.2.1" - bson "^1.1.4" - denque "^1.4.1" - optional-require "^1.0.3" - safe-buffer "^5.1.2" - optionalDependencies: - saslprep "^1.0.0" - -mongodb@3.6.2: - version "3.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb/-/mongodb-3.6.2.tgz#1154a4ac107bf1375112d83a29c5cf97704e96b6" - integrity sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA== - dependencies: - bl "^2.2.1" - bson "^1.1.4" - denque "^1.4.1" - require_optional "^1.0.1" - safe-buffer "^5.1.2" - optionalDependencies: - saslprep "^1.0.0" - -mongodb@^3.6.2: - version "3.7.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongodb/-/mongodb-3.7.1.tgz#492afe1e870eb11b2d98f7e302146d176bda1ed4" - integrity sha512-iSVgexYr8ID0ieeNFUbRfQeOZxOchRck6kEDVySQRaa8VIw/1Pm+/LgcpZcl/BWV6nT0L8lP9qyl7dRPJ6mnLw== - dependencies: - bl "^2.2.1" - bson "^1.1.4" - denque "^1.4.1" - optional-require "^1.0.3" - safe-buffer "^5.1.2" - optionalDependencies: - saslprep "^1.0.0" - -mongoose-legacy-pluralize@1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" - integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ== - -mongoose@^5.12.7, mongoose@^5.6.3: - version "5.13.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mongoose/-/mongoose-5.13.9.tgz#4421a13566daea8fcc80149734c76e5641f477ce" - integrity sha512-JbLw5ie0LJxm7V9LoNxRY//6cyFJf0cOpON2TWUWvF9pabil6ArfECL3xHV2N+mwwO4gXiIa+c0pwTzDUVTgqw== - dependencies: - "@types/bson" "1.x || 4.0.x" - "@types/mongodb" "^3.5.27" - bson "^1.1.4" - kareem "2.3.2" - mongodb "3.6.11" - mongoose-legacy-pluralize "1.0.2" - mpath "0.8.4" - mquery "3.2.5" - ms "2.1.2" - optional-require "1.0.x" - regexp-clone "1.0.0" - safe-buffer "5.2.1" - sift "13.5.2" - sliced "1.0.1" - -morgan@^1.10.0: - version "1.10.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" - integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== - dependencies: - basic-auth "~2.0.1" - debug "2.6.9" - depd "~2.0.0" - on-finished "~2.3.0" - on-headers "~1.0.2" - -mpath@0.8.4: - version "0.8.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mpath/-/mpath-0.8.4.tgz#6b566d9581621d9e931dd3b142ed3618e7599313" - integrity sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g== - -mquery@3.2.5: - version "3.2.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mquery/-/mquery-3.2.5.tgz#8f2305632e4bb197f68f60c0cffa21aaf4060c51" - integrity sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A== - dependencies: - bluebird "3.5.1" - debug "3.1.0" - regexp-clone "^1.0.0" - safe-buffer "5.1.2" - sliced "1.0.1" - -ms@2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@2.1.2: - version "2.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multer@^1.4.2: - version "1.4.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/multer/-/multer-1.4.3.tgz#4db352d6992e028ac0eacf7be45c6efd0264297b" - integrity sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg== - dependencies: - append-field "^1.0.0" - busboy "^0.2.11" - concat-stream "^1.5.2" - mkdirp "^0.5.4" - object-assign "^4.1.1" - on-finished "^2.3.0" - type-is "^1.6.4" - xtend "^4.0.0" - -mute-stream@0.0.5: - version "0.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nanocolors@^0.1.0, nanocolors@^0.1.5: - version "0.1.12" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" - integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ== - -nanoid@^3.1.10: - version "3.1.28" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nanoid/-/nanoid-3.1.28.tgz#3c01bac14cb6c5680569014cc65a2f26424c6bd4" - integrity sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -negotiator@0.6.2: - version "0.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.6.0: - version "2.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -nise@^4.0.4: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" - integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" - -no-case@^2.2.0, no-case@^2.3.2: - version "2.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-addon-api@^3.1.0: - version "3.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== - -node-fetch@^2.6.1: - version "2.6.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" - integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== - dependencies: - whatwg-url "^5.0.0" - -node-forge@^0.10.0: - version "0.10.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - -node-plop@~0.26.2: - version "0.26.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-plop/-/node-plop-0.26.2.tgz#c2523596dab4e28360e615b768b11b4d60d5b1b9" - integrity sha512-q444beWkMvZwAiYC3BRGJUHgRlpOItQHy+xdy6egXg8KjxDY/Ro309spQTNvH01qK9A8XF6pc0xLKbrHDpxW7w== - dependencies: - "@babel/runtime-corejs3" "^7.9.2" - "@types/inquirer" "^6.5.0" - change-case "^3.1.0" - del "^5.1.0" - globby "^10.0.1" - handlebars "^4.4.3" - inquirer "^7.1.0" - isbinaryfile "^4.0.2" - lodash.get "^4.4.2" - mkdirp "^0.5.1" - resolve "^1.12.0" - -node-releases@^1.1.76: - version "1.1.76" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" - integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== - -nodemon@^2.0.3: - version "2.0.13" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nodemon/-/nodemon-2.0.13.tgz#67d40d3a4d5bd840aa785c56587269cfcf5d24aa" - integrity sha512-UMXMpsZsv1UXUttCn6gv8eQPhn6DR4BW+txnL3IN5IHqrCwcrT/yWHfL35UsClGXknTH79r5xbu+6J1zNHuSyA== - dependencies: - chokidar "^3.2.2" - debug "^3.2.6" - ignore-by-default "^1.0.1" - minimatch "^3.0.4" - pstree.remy "^1.1.7" - semver "^5.7.1" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.3" - update-notifier "^5.1.0" - -nopt@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - -nopt@~1.0.10: - version "1.0.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= - dependencies: - abbrev "1" - -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npmlog@^4.1.2: - version "4.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -oauth@0.9.x: - version "0.9.15" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" - integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= - -object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-hash@^2.0.3, object-hash@^2.1.1: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - -object-inspect@^1.9.0: - version "1.11.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0: - version "4.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.defaults@^1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -oidc-provider@^6.29.3: - version "6.31.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/oidc-provider/-/oidc-provider-6.31.1.tgz#2a059fd8437bb8b7bae9a8957a90a895d78a1720" - integrity sha512-YYfcJKGrobdaW2v7bx5crd4yfxFTKAoOTHdQs/gsu6fwzc/yD/qjforQX2VgbTX+ySK8nm7EaAwJuJJSFRo1MA== - dependencies: - "@koa/cors" "^3.1.0" - "@types/koa" "^2.11.4" - debug "^4.1.1" - ejs "^3.1.5" - got "^9.6.0" - jose "^2.0.4" - jsesc "^3.0.1" - koa "^2.13.0" - koa-compose "^4.1.0" - lru-cache "^6.0.0" - nanoid "^3.1.10" - object-hash "^2.0.3" - oidc-token-hash "^5.0.1" - raw-body "^2.4.1" - -oidc-token-hash@^5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" - integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== - -on-finished@^2.3.0, on-finished@~2.3.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -onetime@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -only@~0.0.2: - version "0.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - -optional-require@1.0.x: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/optional-require/-/optional-require-1.0.3.tgz#275b8e9df1dc6a17ad155369c2422a440f89cb07" - integrity sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA== - -optional-require@^1.0.3: - version "1.1.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/optional-require/-/optional-require-1.1.8.tgz#16364d76261b75d964c482b2406cb824d8ec44b7" - integrity sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA== - dependencies: - require-at "^1.0.6" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -ora@^3.4.0: - version "3.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== - dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-each-series@^2.1.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" - integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.1: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json@^6.3.0: - version "6.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - -param-case@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -parse5@6.0.1: - version "6.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parseurl@^1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascal-case@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" - integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= - dependencies: - camel-case "^3.0.0" - upper-case-first "^1.1.0" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -passport-azure-ad-oauth2@0.0.4: - version "0.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-azure-ad-oauth2/-/passport-azure-ad-oauth2-0.0.4.tgz#1a09d8869efd4afd095116e228941961b3a5c120" - integrity sha1-GgnYhp79Sv0JURbiKJQZYbOlwSA= - dependencies: - passport-oauth "1.0.x" - -passport-google-oauth1@1.x.x: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc" - integrity sha1-r3SoA99R7GRvZqRNgigr5vEI4Mw= - dependencies: - passport-oauth1 "1.x.x" - -passport-google-oauth20@2.x.x: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz#0d241b2d21ebd3dc7f2b60669ec4d587e3a674ef" - integrity sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ== - dependencies: - passport-oauth2 "1.x.x" - -passport-google-oauth@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz#f6eb4bc96dd6c16ec0ecfdf4e05ec48ca54d4dae" - integrity sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA== - dependencies: - passport-google-oauth1 "1.x.x" - passport-google-oauth20 "2.x.x" - -passport-jwt@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065" - integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg== - dependencies: - jsonwebtoken "^8.2.0" - passport-strategy "^1.0.0" - -passport-linkedin-oauth2@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-linkedin-oauth2/-/passport-linkedin-oauth2-2.0.0.tgz#c320374baa44c1412a9b023590d1d0c1c77cfe73" - integrity sha512-PnSeq2HzFQ/y1/p2RTF/kG2zhJ7kwGVg4xO3E+JNxz2aI0pFJGAqC503FVpUksYbhQdNhL6QYlK9qrEXD7ZYCg== - dependencies: - passport-oauth2 "1.x.x" - -passport-oauth1@1.x.x: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-oauth1/-/passport-oauth1-1.2.0.tgz#5229d431781bf5b265bec86ce9a9cce58a756cf9" - integrity sha512-Sv2YWodC6jN12M/OXwmR4BIXeeIHjjbwYTQw4kS6tHK4zYzSEpxBgSJJnknBjICA5cj0ju3FSnG1XmHgIhYnLg== - dependencies: - oauth "0.9.x" - passport-strategy "1.x.x" - utils-merge "1.x.x" - -passport-oauth2@1.x.x: - version "1.6.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b" - integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ== - dependencies: - base64url "3.x.x" - oauth "0.9.x" - passport-strategy "1.x.x" - uid2 "0.0.x" - utils-merge "1.x.x" - -passport-oauth@1.0.x: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-oauth/-/passport-oauth-1.0.0.tgz#90aff63387540f02089af28cdad39ea7f80d77df" - integrity sha1-kK/2M4dUDwIImvKM2tOep/gNd98= - dependencies: - passport-oauth1 "1.x.x" - passport-oauth2 "1.x.x" - -passport-openidconnect@0.0.2: - version "0.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-openidconnect/-/passport-openidconnect-0.0.2.tgz#e488f8bdb386c9a9fd39c91d5ab8c880156e8153" - integrity sha1-5Ij4vbOGyan9OckdWrjIgBVugVM= - dependencies: - oauth "0.9.x" - passport-strategy "1.x.x" - request "^2.75.0" - webfinger "0.4.x" - -passport-strategy@1.x.x, passport-strategy@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" - integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= - -passport@^0.4.1: - version "0.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" - integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== - dependencies: - passport-strategy "1.x.x" - pause "0.0.1" - -path-case@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" - integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= - dependencies: - no-case "^2.2.0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pause@0.0.1: - version "0.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" - integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10= - -pend@~1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -pify@^2.0.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pirates@^4.0.1: - version "4.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - -pkg-dir@^4.1.0, pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -plop@^2.7.4: - version "2.7.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/plop/-/plop-2.7.4.tgz#835280aef3541102063b758f5116cce899e1c72b" - integrity sha512-SaqN3mwug/Ur2RE/ryo05oLTLy+8qZGwosNt9JnrFWca+dLCsPJR1j2ZXwjrccmNu6LA7eB56lRyk/G0fKf9HA== - dependencies: - "@types/liftoff" "^2.5.0" - chalk "^1.1.3" - interpret "^1.2.0" - liftoff "^2.5.0" - minimist "^1.2.0" - node-plop "~0.26.2" - ora "^3.4.0" - v8flags "^2.0.10" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -prettier@^2.2.1: - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" - integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== - -pretty-format@^26.6.2: - version "26.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.0: - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -prompts@^2.0.1: - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" - integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -proto3-json-serializer@^0.1.1: - version "0.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/proto3-json-serializer/-/proto3-json-serializer-0.1.4.tgz#aa2dc4c9c9b7ea05631354b2c2e52c227539a7f0" - integrity sha512-bFzdsKU/zaTobWrRxRniMZIzzcgKYlmBWL1gAcTXZ2M7TQTGPI0JoYYs6bN7tpWj59ZCfwg7Ii/A2e8BbQGYnQ== - -protobufjs@6.11.2, protobufjs@^6.10.0: - version "6.11.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" - integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - -proxy-addr@~2.0.5: - version "2.0.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -pstree.remy@^1.1.7: - version "1.1.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" - integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== - -pump@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^2.0.0: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" - integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== - dependencies: - duplexify "^4.1.1" - inherits "^2.0.3" - pump "^3.0.0" - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -pupa@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" - -qs@6.7.0: - version "6.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@^6.5.1, qs@^6.7.0: - version "6.10.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" - integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== - dependencies: - side-channel "^1.0.4" - -qs@~6.5.2: - version "6.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -query-string@^6.12.1: - version "6.14.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -random-bytes@~1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" - integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= - -randombytes@2.0.3: - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" - integrity sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew= - -randomstring@^1.1.5: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/randomstring/-/randomstring-1.2.1.tgz#71cd3cda24ad1b7e0b65286b3aa5c10853019349" - integrity sha512-eMnfell9XuU3jfCx3f4xCaFAt0YMFPZhx9R3PSStmLarDKg5j5vivqKhf/8pvG+VX/YkxsckHK/VPUrKa5V07A== - dependencies: - array-uniq "1.0.2" - randombytes "2.0.3" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -raw-body@^2.4.1: - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.2.8: - version "1.2.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -react-is@^17.0.1: - version "17.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - -readable-stream@1.1.x: - version "1.1.14" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.5: - version "2.3.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -readline2@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -regenerate-unicode-properties@^9.0.0: - version "9.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" - integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp-clone@1.0.0, regexp-clone@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63" - integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw== - -regexpp@^3.1.0: - version "3.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -regexpu-core@^4.7.1: - version "4.8.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" - integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^9.0.0" - regjsgen "^0.5.2" - regjsparser "^0.7.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== - dependencies: - rc "^1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -regjsgen@^0.5.2: - version "0.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsparser@^0.7.0: - version "0.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" - integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== - dependencies: - jsesc "~0.5.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -request@^2.75.0, request@^2.88.0: - version "2.88.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-at@^1.0.6: - version "1.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require-at/-/require-at-1.0.6.tgz#9eb7e3c5e00727f5a4744070a7f560d4de4f6e6a" - integrity sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -require_optional@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" - integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g== - dependencies: - resolve-from "^2.0.0" - semver "^5.1.0" - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" - integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.18.1: - version "1.20.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry-request@^4.0.0, retry-request@^4.2.2: - version "4.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" - integrity sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg== - dependencies: - debug "^4.1.1" - extend "^3.0.2" - -retry@0.13.1: - version "0.13.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rndm@1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rndm/-/rndm-1.2.0.tgz#f33fe9cfb52bbfd520aa18323bc65db110a1b76c" - integrity sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w= - -rsvp@^4.8.4: - version "4.8.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -run-async@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= - dependencies: - once "^1.3.0" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= - -rxjs@^6.4.0, rxjs@^6.6.0: - version "6.6.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sane@^4.0.3: - version "4.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - -saslprep@^1.0.0: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" - integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== - dependencies: - sparse-bitfield "^3.0.3" - -sax@>=0.1.1: - version "1.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: - version "5.7.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: - version "7.3.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -send@0.17.1: - version "0.17.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -sentence-case@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" - integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= - dependencies: - no-case "^2.2.0" - upper-case-first "^1.1.2" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shellwords@^0.1.1: - version "0.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -sift@13.5.2: - version "13.5.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sift/-/sift-13.5.2.tgz#24a715e13c617b086166cd04917d204a591c9da6" - integrity sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA== - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/signal-exit/-/signal-exit-3.0.4.tgz#366a4684d175b9cab2081e3681fda3747b6c51d7" - integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q== - -sinon@^9.2.4: - version "9.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" - integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== - dependencies: - "@sinonjs/commons" "^1.8.1" - "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/samsam" "^5.3.1" - diff "^4.0.2" - nise "^4.0.4" - supports-color "^7.1.0" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -sliced@1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" - integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E= - -snake-case@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" - integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= - dependencies: - no-case "^2.2.0" - -snakeize@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d" - integrity sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0= - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -snyk@^1.334.0: - version "1.722.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/snyk/-/snyk-1.722.0.tgz#dafa9b204514c7bde8620618793873e3a300179c" - integrity sha512-EelrKO6Gv4r5Cs7XrOY4ZP6ZF59EaB0ln2gJRzPhxMJ1s5oBhly/Lojl9aZzgXDfFohm6fDa0kpCr+dQRtCT1g== - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.5.6: - version "0.5.20" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.0, source-map@^0.5.6: - version "0.5.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.3: - version "0.7.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -sparse-bitfield@^3.0.3: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" - integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= - dependencies: - memory-pager "^1.0.2" - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.10" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" - integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-utils@^2.0.2: - version "2.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== - dependencies: - escape-string-regexp "^2.0.0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.5.0 < 2", statuses@^1.5.0, statuses@~1.5.0: - version "1.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -step@0.0.x: - version "0.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" - integrity sha1-FD54SaXX0/SgiP4pr5SRUhbu7eI= - -stream-events@^1.0.1, stream-events@^1.0.4, stream-events@^1.0.5: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" - integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== - dependencies: - stubs "^3.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -streamsearch@0.1.2: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - -string-length@^4.0.1: - version "4.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: - version "4.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -stubs@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" - integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= - -superagent@^3.8.3: - version "3.8.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" - integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== - dependencies: - component-emitter "^1.2.0" - cookiejar "^2.1.0" - debug "^3.1.0" - extend "^3.0.0" - form-data "^2.3.1" - formidable "^1.2.0" - methods "^1.1.1" - mime "^1.4.1" - qs "^6.5.1" - readable-stream "^2.3.5" - -supertest@^4.0.2: - version "4.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36" - integrity sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ== - dependencies: - methods "^1.1.2" - superagent "^3.8.3" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0, supports-color@^7.1.0: - version "7.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - -swagger-ui-dist@^3.18.1: - version "3.52.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/swagger-ui-dist/-/swagger-ui-dist-3.52.3.tgz#a09b5cdccac69e3f5f1cbd258654a110119a7f0e" - integrity sha512-7QSY4milmYx5O8dbzU5tTftiaoZt+4JGxahTTBiLAnbTvhTyzum9rsjDIJjC+xeT8Tt1KfB38UuQQjmrh2THDQ== - -swagger-ui-express@^4.1.4: - version "4.1.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz#682294af3d5c70f74a1fa4d6a9b503a9ee55ea82" - integrity sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw== - dependencies: - swagger-ui-dist "^3.18.1" - -swap-case@^1.1.0: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" - integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= - dependencies: - lower-case "^1.1.1" - upper-case "^1.1.1" - -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -table@^6.0.9: - version "6.7.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar@^4.0.2: - version "4.4.19" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - -tar@^6.1.0: - version "6.1.11" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -teeny-request@^7.0.0: - version "7.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/teeny-request/-/teeny-request-7.1.2.tgz#69f8d4c3c2958cf0040f9a5c32a5c9db591d412a" - integrity sha512-Mr4NYZuniKDpgcLxdBkDE1CcWy98Aw1ennn6oNofen+XWUvDs+ZZzBAujy6XOAVwwLLZMwEQSfdljUI+ebs4Ww== - dependencies: - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - stream-events "^1.0.5" - uuid "^8.0.0" - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -test@^0.6.0: - version "0.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/test/-/test-0.6.0.tgz#5986ac445ec17754322512d104ba32c8a63e938e" - integrity sha1-WYasRF7Bd1QyJRLRBLoyyKY+k44= - dependencies: - ansi-font "0.0.2" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -throat@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== - -through@^2.3.6: - version "2.3.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -title-case@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" - integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= - dependencies: - no-case "^2.2.0" - upper-case "^1.0.3" - -tmp@^0.0.33: - version "0.0.33" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -tmp@^0.2.1: - version "0.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -tmpl@1.0.x: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -touch@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -transformobject@^0.3.1: - version "0.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/transformobject/-/transformobject-0.3.1.tgz#10af370a5485e7bcc04cbe721d763fdecdbcecc3" - integrity sha512-jMjVJOb28yntdVoJBoZGWLuLugCJlRQklCAewTrOiQL6Y9KHWmu0BRgS1Qi1RX3aU4/RrUzdPuHcc9t9v3TlCA== - -tslib@^1.9.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tsscmp@1.0.6: - version "1.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" - integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8, type-detect@^4.0.8: - version "4.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.6.0: - version "0.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -uglify-js@^3.1.4: - version "3.14.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99" - integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== - -uid-safe@2.1.5, uid-safe@~2.1.5: - version "2.1.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" - integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== - dependencies: - random-bytes "~1.0.0" - -uid2@0.0.x: - version "0.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" - integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - -undefsafe@^2.0.3: - version "2.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" - integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== - dependencies: - debug "^2.2.0" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -unique-string@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -universalify@^0.1.2: - version "0.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -update-notifier@^5.1.0: - version "5.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" - integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== - dependencies: - boxen "^5.0.0" - chalk "^4.1.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.4.0" - is-npm "^5.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.1.0" - pupa "^2.1.1" - semver "^7.3.4" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - -upper-case-first@^1.1.0, upper-case-first@^1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" - integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= - dependencies: - upper-case "^1.1.1" - -upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -url-template@^2.0.8: - version "2.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" - integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE= - -use@^3.1.0: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -user-home@^1.1.1: - version "1.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -utils-merge@1.0.1, utils-merge@1.x.x: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@8.3.0: - version "8.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" - integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== - -uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.1: - version "8.3.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^3.3.2: - version "3.4.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -v8-to-istanbul@^7.0.0: - version "7.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" - integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" - -v8flags@^2.0.10: - version "2.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" - integrity sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ= - dependencies: - user-home "^1.1.1" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validator@^13.5.2: - version "13.6.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" - integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== - -vary@^1, vary@^1.1.2, vary@~1.1.2: - version "1.1.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -verror@1.10.0: - version "1.10.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - -walker@^1.0.7, walker@~1.0.5: - version "1.0.7" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= - dependencies: - makeerror "1.0.x" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - -webfinger@0.4.x: - version "0.4.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" - integrity sha1-NHem2XeZRhiWA5/P/GULc0aO520= - dependencies: - step "0.0.x" - xml2js "0.1.x" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -widest-line@^3.1.0: - version "3.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - -window-size@^0.2.0: - version "0.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -ws@^7.4.6: - version "7.5.5" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== - -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xml2js@0.1.x: - version "0.1.14" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" - integrity sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw= - dependencies: - sax ">=0.1.1" - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xtend@^4.0.0: - version "4.0.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^3.2.1: - version "3.2.2" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.0, yallist@^3.1.1: - version "3.1.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yamljs@^0.3.0: - version "0.3.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" - integrity sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ== - dependencies: - argparse "^1.0.7" - glob "^7.0.5" - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^2.4.1: - version "2.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" - integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= - dependencies: - camelcase "^3.0.0" - lodash.assign "^4.0.6" - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs@^15.4.1: - version "15.4.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^16.1.1: - version "16.2.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^4.8.1: - version "4.8.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" - integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= - dependencies: - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - lodash.assign "^4.0.3" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.1" - which-module "^1.0.0" - window-size "^0.2.0" - y18n "^3.2.1" - yargs-parser "^2.4.1" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -ylru@^1.2.0: - version "1.2.1" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" - integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://affinitywater-633033879498.d.codeartifact.eu-west-1.amazonaws.com:443/npm/react-core-ui/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 9e57b34cca905804955896809e6885bb44bb4baf Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 14 Apr 2022 09:53:25 +0100 Subject: [PATCH 055/389] add federation warning to rejected datasets email --- .../utilities/emailGenerator.util.js | 32 +++++++++++++------ src/utils/datasetonboarding.util.js | 9 +++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 356bdb43..4060bf65 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2083,9 +2083,10 @@ const _generateMetadataOnboardingApproved = options => { }; const _generateMetadataOnboardingRejected = options => { - let { name, publisherId, comment } = options; + let { name, publisherId, comment, isFederated } = options; let commentHTML = ''; + let federatedMessageHTML = ''; if (!_.isEmpty(comment)) { commentHTML = ` @@ -2100,6 +2101,14 @@ const _generateMetadataOnboardingRejected = options => { `; } + if (!_.isUndefined(isFederated) && isFederated) { + federatedMessageHTML = ` + + It is important that you update these changes in your metadata catalogue. Do not apply these changes directly to the Gateway as this ability has been disabled for federated datasets. + + `; + } + let body = `
{ + ${federatedMessageHTML} ${commentHTML} @@ -2611,15 +2621,19 @@ const _generateWordAttachment = async (templateName, questionAnswers) => { return wordAttachment; }; -const _generateWordContent = async (filename) => { - let pathToAttachment = `${__dirname}/populatedtemplate.docx`; - let content = await fs.readFileSync(pathToAttachment).toString('base64'); - return content -} +const _generateWordContent = async filename => { + let pathToAttachment = `${__dirname}/populatedtemplate.docx`; + let content = await fs.readFileSync(pathToAttachment).toString('base64'); + return content; +}; const _deleteWordAttachmentTempFiles = async () => { - if(fs.existsSync(`${__dirname}/template.docx`)){fs.unlinkSync(__dirname + '/template.docx')} - if(fs.existsSync(`${__dirname}/populatedtemplate.docx`)){fs.unlinkSync(__dirname + '/populatedtemplate.docx')} + if (fs.existsSync(`${__dirname}/template.docx`)) { + fs.unlinkSync(__dirname + '/template.docx'); + } + if (fs.existsSync(`${__dirname}/populatedtemplate.docx`)) { + fs.unlinkSync(__dirname + '/populatedtemplate.docx'); + } }; export default { @@ -2648,7 +2662,7 @@ export default { generateNewDARMessage: _generateNewDARMessage, deleteWordAttachmentTempFiles: _deleteWordAttachmentTempFiles, generateWordAttachment: _generateWordAttachment, - generateWordContent: _generateWordContent, + generateWordContent: _generateWordContent, //Workflows generateWorkflowAssigned: _generateWorkflowAssigned, generateWorkflowCreated: _generateWorkflowCreated, diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 51f09e84..3695ea48 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -921,7 +921,11 @@ const createNotifications = async (type, context) => { break; case constants.notificationTypes.DATASETREJECTED: // 1. Get user removed - team = await TeamModel.findOne({ _id: context.datasetv2.summary.publisher.identifier }).lean(); + team = await TeamModel.findOne({ _id: context.datasetv2.summary.publisher.identifier }) + .populate([{ path: 'publisher' }]) + .lean(); + + const isFederated = !_.isUndefined(team.publisher.federation) && team.publisher.federation.active; for (let member of team.members) { teamMembers.push(member.memberid); @@ -948,8 +952,11 @@ const createNotifications = async (type, context) => { name: context.name, publisherId: context.datasetv2.summary.publisher.identifier, comment: context.applicationStatusDesc, + isFederated, }; + html = emailGenerator.generateMetadataOnboardingRejected(options); + emailGenerator.sendEmail( teamMembersDetails, constants.hdrukEmail, From 236941541b6ea50f187ec2f26f9b80193615187d Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 14 Apr 2022 09:54:00 +0100 Subject: [PATCH 056/389] tests for rejected dataset emails --- .../__tests__/emailGenerator.util.test.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/resources/utilities/__tests__/emailGenerator.util.test.js diff --git a/src/resources/utilities/__tests__/emailGenerator.util.test.js b/src/resources/utilities/__tests__/emailGenerator.util.test.js new file mode 100644 index 00000000..184b63b8 --- /dev/null +++ b/src/resources/utilities/__tests__/emailGenerator.util.test.js @@ -0,0 +1,25 @@ +import emailGenerator from '../emailGenerator.util'; + +describe('Email generator utility functions', () => { + describe('_generateMetadataOnboardingRejected', () => { + let isFederated; + + it('SHOULD include federated warning if isFederated is true', async () => { + isFederated = true; + + const emailBody = emailGenerator.generateMetadataOnboardingRejected({ isFederated }); + + // Federated warning should be present if dataset if from a federated publisher + expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(true); + }); + + it('SHOULD NOT include federated warning if isFederated is false', async () => { + isFederated = false; + + const emailBody = emailGenerator.generateMetadataOnboardingRejected({ isFederated }); + + // Federated warning should not be present if dataset is not from a federated publisher + expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(false); + }); + }); +}); From 69ae41856db0460a3a58b99b902c741734f53fab Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 14 Apr 2022 10:00:29 +0100 Subject: [PATCH 057/389] typo fix --- src/resources/utilities/__tests__/emailGenerator.util.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/utilities/__tests__/emailGenerator.util.test.js b/src/resources/utilities/__tests__/emailGenerator.util.test.js index 184b63b8..e8a34ada 100644 --- a/src/resources/utilities/__tests__/emailGenerator.util.test.js +++ b/src/resources/utilities/__tests__/emailGenerator.util.test.js @@ -9,7 +9,7 @@ describe('Email generator utility functions', () => { const emailBody = emailGenerator.generateMetadataOnboardingRejected({ isFederated }); - // Federated warning should be present if dataset if from a federated publisher + // Federated warning should be present if dataset is from a federated publisher expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(true); }); From b2429db8713bfc0706d4bca56f2d9447c23d2dba Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 14 Apr 2022 12:52:33 +0100 Subject: [PATCH 058/389] update message --- src/resources/team/team.controller.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 175ef1c9..531a12c1 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -1,4 +1,4 @@ -import { isEmpty, has, difference, includes, isNull, filter, some } from 'lodash'; +import _, { isEmpty, has, difference, includes, isNull, filter, some } from 'lodash'; import { TeamModel } from './team.model'; import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; @@ -1047,8 +1047,12 @@ const filterMembersByNoticationTypes = (members, notificationTypes) => { */ const filterMembersByNoticationTypesOptIn = (members, notificationTypes) => { return filter(members, member => { + if (!('notifications' in member) || _.isEmpty(member.notifications)) { + return true; + } + return some(member.notifications, notification => { - return includes(notificationTypes, notification.notificationType) && notification.optIn; + return includes(notificationTypes, notification.notificationType) && (notification.optIn === true); }); }); }; From 0378f484d2c7258bbcdf1cc2f0ce6be3af1b2c48 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 21 Apr 2022 12:20:03 +0100 Subject: [PATCH 059/389] hotfix --- src/resources/user/user.route.js | 6 ++++-- src/resources/user/user.service.js | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/resources/user/user.route.js b/src/resources/user/user.route.js index 0bed1929..316c3113 100644 --- a/src/resources/user/user.route.js +++ b/src/resources/user/user.route.js @@ -31,7 +31,8 @@ router.get('/:userID', passport.authenticate('jwt'), utils.checkIsUser(), async // @desc get all // @access Private router.get('/', passport.authenticate('jwt'), async (req, res) => { - await getUsers(req.user.id) + let reqUserId = req.user.id; + await getUsers(reqUserId) .then(response => { return res.json({ success: true, data: response }); }) @@ -45,7 +46,8 @@ router.get('/', passport.authenticate('jwt'), async (req, res) => { // @access Private router.get('/search/:filter', passport.authenticate('jwt'), [checkInputMiddleware, checkMinLengthMiddleware], async (req, res) => { let filterString = req.params.filter; - await getUsers(null) + let reqUserId = req.user.id; + await getUsers(reqUserId, filterString) .then(response => { const usersFiltered = []; diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 97b78ece..d4b2a903 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -100,6 +100,7 @@ export const getUsersCollaborators = async (currentUserId) => { // Get all collaborators from tools and papers (data collection) filter = currentUserId ? { authors: currentUserId } : {}; + // filter = { authors: currentUserId }; await getCollaboratorsTools(filter, currentUserId); // Get all collaborators from DARs @@ -164,7 +165,7 @@ export const populateCollaborators = async (collaboratorsEntity, items, currentU return arrCollaborators; } -export const getUsers = async (currentUserId) => { +export const getUsers = async (currentUserId, filterString = null) => { // Get the users collaborators arrCollaborators = []; let usersCollaborators = await getUsersCollaborators(currentUserId); @@ -252,7 +253,7 @@ export const getUsers = async (currentUserId) => { return collaborator.user; }); - if (currentUserId) { + if (!filterString) { resolve([...collaboratorUsers]); } else { resolve([...collaboratorUsers, ...nonCollaboratorUsers]); From 2f1ce4bc226ec1a749d35b6d86d6ac701f017a88 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 21 Apr 2022 13:02:21 +0100 Subject: [PATCH 060/389] DUR edit bug fix --- src/resources/dataUseRegister/dataUseRegister.route.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.route.js b/src/resources/dataUseRegister/dataUseRegister.route.js index 5b63ad54..a4363306 100644 --- a/src/resources/dataUseRegister/dataUseRegister.route.js +++ b/src/resources/dataUseRegister/dataUseRegister.route.js @@ -86,7 +86,8 @@ const authorizeUpdate = async (req, res, next) => { } const { publisher } = dataUseRegister; - const authorised = isUserDataUseAdmin(requestingUser) || isUserMemberOfTeam(requestingUser, publisher._id); + const isAuthor = requestingUser._id.toString() === dataUseRegister.user.toString(); + const authorised = isUserDataUseAdmin(requestingUser) || isUserMemberOfTeam(requestingUser, publisher._id) || isAuthor; if (!authorised) { return res.status(401).json({ success: false, From 9c8e17520ecbd1d7d535d75ec9902589c02070d4 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 21 Apr 2022 13:15:51 +0100 Subject: [PATCH 061/389] DUR edit bug fix --- src/middlewares/dataUseRegister.middleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/middlewares/dataUseRegister.middleware.js b/src/middlewares/dataUseRegister.middleware.js index 7d3e6fa5..020dc0f6 100644 --- a/src/middlewares/dataUseRegister.middleware.js +++ b/src/middlewares/dataUseRegister.middleware.js @@ -77,7 +77,8 @@ const authorizeUpdate = async (req, res, next) => { } const { publisher } = dataUseRegister; - const authorised = _isUserDataUseAdmin(requestingUser) || _isUserMemberOfTeam(requestingUser, publisher._id); + const isAuthor = requestingUser._id.toString() === dataUseRegister.user.toString(); + const authorised = _isUserDataUseAdmin(requestingUser) || _isUserMemberOfTeam(requestingUser, publisher._id) || isAuthor; if (!authorised) { return res.status(401).json({ success: false, From b3b1f1dfb00ff49e289d244284b2b21410900a2f Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 21 Apr 2022 13:27:56 +0100 Subject: [PATCH 062/389] DUR edit bug fix --- .../__tests__/dataUseRegister.middleware.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/middlewares/__tests__/dataUseRegister.middleware.test.js b/src/middlewares/__tests__/dataUseRegister.middleware.test.js index e05bc752..a70de1a9 100644 --- a/src/middlewares/__tests__/dataUseRegister.middleware.test.js +++ b/src/middlewares/__tests__/dataUseRegister.middleware.test.js @@ -142,7 +142,7 @@ describe('Testing the dataUserRegister middleware', () => { ], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ publisher: 'testPublisher' }); + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ publisher: 'testPublisher', user: 'anotherTestUser' }); await authorizeUpdate(req, res, nextFunction); @@ -170,7 +170,7 @@ describe('Testing the dataUserRegister middleware', () => { teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ projectIdText: 'testIdText' }); + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ projectIdText: 'testIdText', user: 'testUser' }); await authorizeUpdate(req, res, nextFunction); @@ -198,7 +198,7 @@ describe('Testing the dataUserRegister middleware', () => { teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'datasetTitles' }); + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'datasetTitles', user: 'testUser' }); await authorizeUpdate(req, res, nextFunction); @@ -227,7 +227,9 @@ describe('Testing the dataUserRegister middleware', () => { teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'match', projectIdText: 'match' }); + sinon + .stub(dataUseRegisterService, 'getDataUseRegister') + .returns({ datasetTitles: 'match', projectIdText: 'match', user: 'testUser' }); await authorizeUpdate(req, res, nextFunction); From 9e103c526586b7bd48a64eeee29f6c0fdcd8e115 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 22 Apr 2022 12:19:00 +0100 Subject: [PATCH 063/389] GAT-928 fix --- .../__tests__/dataUseRegister.middleware.test.js | 10 ++++++---- src/middlewares/dataUseRegister.middleware.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/middlewares/__tests__/dataUseRegister.middleware.test.js b/src/middlewares/__tests__/dataUseRegister.middleware.test.js index a70de1a9..4c60927b 100644 --- a/src/middlewares/__tests__/dataUseRegister.middleware.test.js +++ b/src/middlewares/__tests__/dataUseRegister.middleware.test.js @@ -142,7 +142,9 @@ describe('Testing the dataUserRegister middleware', () => { ], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ publisher: 'testPublisher', user: 'anotherTestUser' }); + sinon + .stub(dataUseRegisterService, 'getDataUseRegister') + .returns({ publisher: 'testPublisher', gatewayApplicants: ['anotherTestUser'] }); await authorizeUpdate(req, res, nextFunction); @@ -170,7 +172,7 @@ describe('Testing the dataUserRegister middleware', () => { teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ projectIdText: 'testIdText', user: 'testUser' }); + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ projectIdText: 'testIdText', gatewayApplicants: ['testUser'] }); await authorizeUpdate(req, res, nextFunction); @@ -198,7 +200,7 @@ describe('Testing the dataUserRegister middleware', () => { teams: [{ publisher: { _id: 'testPublisher' }, type: 'admin', members: [{ memberid: 'testUser', roles: 'admin_data_use' }] }], }; - sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'datasetTitles', user: 'testUser' }); + sinon.stub(dataUseRegisterService, 'getDataUseRegister').returns({ datasetTitles: 'datasetTitles', gatewayApplicants: ['testUser'] }); await authorizeUpdate(req, res, nextFunction); @@ -229,7 +231,7 @@ describe('Testing the dataUserRegister middleware', () => { sinon .stub(dataUseRegisterService, 'getDataUseRegister') - .returns({ datasetTitles: 'match', projectIdText: 'match', user: 'testUser' }); + .returns({ datasetTitles: 'match', projectIdText: 'match', gatewayApplicants: ['testUser'] }); await authorizeUpdate(req, res, nextFunction); diff --git a/src/middlewares/dataUseRegister.middleware.js b/src/middlewares/dataUseRegister.middleware.js index 020dc0f6..5bc90f36 100644 --- a/src/middlewares/dataUseRegister.middleware.js +++ b/src/middlewares/dataUseRegister.middleware.js @@ -77,7 +77,7 @@ const authorizeUpdate = async (req, res, next) => { } const { publisher } = dataUseRegister; - const isAuthor = requestingUser._id.toString() === dataUseRegister.user.toString(); + const isAuthor = dataUseRegister.gatewayApplicants.includes(requestingUser._id); const authorised = _isUserDataUseAdmin(requestingUser) || _isUserMemberOfTeam(requestingUser, publisher._id) || isAuthor; if (!authorised) { return res.status(401).json({ From b288b7e3e5ef0763765d58d92b5e2bb90025d045 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 15:02:07 +0100 Subject: [PATCH 064/389] update autosuggest --- src/resources/user/user.service.js | 166 ++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 52 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index d4b2a903..9d53f59d 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -95,8 +95,8 @@ export const getUsersCollaborators = async (currentUserId) => { await getCollaboratorsCollections(filter, currentUserId); // Get all collaborators from cohorts - filter = currentUserId ? { uploaders: currentUserId } : {}; - await getCollaboratorsCohorts(filter, currentUserId); + // filter = currentUserId ? { uploaders: currentUserId } : {}; + // await getCollaboratorsCohorts(filter, currentUserId); // Get all collaborators from tools and papers (data collection) filter = currentUserId ? { authors: currentUserId } : {}; @@ -168,62 +168,128 @@ export const populateCollaborators = async (collaboratorsEntity, items, currentU export const getUsers = async (currentUserId, filterString = null) => { // Get the users collaborators arrCollaborators = []; - let usersCollaborators = await getUsersCollaborators(currentUserId); + let usersCollaborators; + if (!filterString) { + usersCollaborators = await getUsersCollaborators(currentUserId); + } else { + usersCollaborators = new Map(); + } // Get the whole list of users - var q = Data.aggregate([ - // Find all tools with type of person - { $match: { type: 'person' } }, - // Perform lookup to users - { - $lookup: { - from: 'users', - localField: 'id', - foreignField: 'id', - as: 'user', + let typePerson; + if (filterString) { + const regex = /(?=\S*[-])([a-zA-Z'-]+)/g; + // Surround matching words in quotation marks + filterString = filterString.replace(regex, '"$1"'); + typePerson = Data.aggregate([ + // Find all tools with type of person + { $match: { type: 'person' } }, + // Perform lookup to users + { + $lookup: { + from: 'users', + localField: 'id', + foreignField: 'id', + as: 'user', + }, + }, + { + $match: { + $or: [ + { 'user.firstname': {'$regex': `${filterString}`, '$options': 'i'} }, + { 'user.lastname': {'$regex': `${filterString}`, '$options': 'i'} }, + ] + } + }, + // select fields to use + { + $project: { + _id: '$user._id', + id: 1, + firstname: 1, + lastname: 1, + orcid: { + $cond: [ + { + $eq: [true, '$showOrcid'], + }, + '$orcid', + '$$REMOVE', + ], + }, + bio: { + $cond: [ + { + $eq: [true, '$showBio'], + }, + '$bio', + '$$REMOVE', + ], + }, + email: '$user.email', + }, + }, + { + $sort: { + updateAt: -1 + }, }, - }, - // select fields to use - { - $project: { - _id: '$user._id', - id: 1, - firstname: 1, - lastname: 1, - orcid: { - $cond: [ - { - $eq: [true, '$showOrcid'], - }, - '$orcid', - '$$REMOVE', - ], + ]); + } else { + typePerson = Data.aggregate([ + // Find all tools with type of person + { $match: { type: 'person' } }, + // Perform lookup to users + { + $lookup: { + from: 'users', + localField: 'id', + foreignField: 'id', + as: 'user', }, - bio: { - $cond: [ - { - $eq: [true, '$showBio'], - }, - '$bio', - '$$REMOVE', - ], + }, + // select fields to use + { + $project: { + _id: '$user._id', + id: 1, + firstname: 1, + lastname: 1, + orcid: { + $cond: [ + { + $eq: [true, '$showOrcid'], + }, + '$orcid', + '$$REMOVE', + ], + }, + bio: { + $cond: [ + { + $eq: [true, '$showBio'], + }, + '$bio', + '$$REMOVE', + ], + }, + email: '$user.email', }, - email: '$user.email', }, - }, - { - $sort: { - updateAt: -1 + { + $sort: { + updateAt: -1 + }, }, - }, - ]); + ]); + } return new Promise((resolve, reject) => { - q.exec((err, data) => { + typePerson.exec((err, data) => { if (err) { return err; } - + console.log(`data : ${JSON.stringify(data)}`); const users = []; data.map(dat => { let { _id, id, firstname, lastname, orcid = '', bio = '', email = '' } = dat; @@ -245,7 +311,7 @@ export const getUsers = async (currentUserId, filterString = null) => { } }); } - + console.log(`collaborators : ${JSON.stringify(collaborators)}`); collaborators.sort((a, b) => b.count - a.count); // Remove count after collaborators are sorted @@ -253,11 +319,7 @@ export const getUsers = async (currentUserId, filterString = null) => { return collaborator.user; }); - if (!filterString) { - resolve([...collaboratorUsers]); - } else { - resolve([...collaboratorUsers, ...nonCollaboratorUsers]); - } + resolve([...collaboratorUsers, ...nonCollaboratorUsers]); }); }); } From e9de21c29331483d9733ca0dc1e62e4d59ebabec Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 15:03:08 +0100 Subject: [PATCH 065/389] update --- src/resources/user/user.service.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 9d53f59d..63513982 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -178,9 +178,6 @@ export const getUsers = async (currentUserId, filterString = null) => { // Get the whole list of users let typePerson; if (filterString) { - const regex = /(?=\S*[-])([a-zA-Z'-]+)/g; - // Surround matching words in quotation marks - filterString = filterString.replace(regex, '"$1"'); typePerson = Data.aggregate([ // Find all tools with type of person { $match: { type: 'person' } }, From 30f5204a1b2284ede005c3bf37ede6ced36c45e1 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 15:04:22 +0100 Subject: [PATCH 066/389] remove console --- src/resources/user/user.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 63513982..22bfdd34 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -286,7 +286,7 @@ export const getUsers = async (currentUserId, filterString = null) => { if (err) { return err; } - console.log(`data : ${JSON.stringify(data)}`); + const users = []; data.map(dat => { let { _id, id, firstname, lastname, orcid = '', bio = '', email = '' } = dat; @@ -308,7 +308,7 @@ export const getUsers = async (currentUserId, filterString = null) => { } }); } - console.log(`collaborators : ${JSON.stringify(collaborators)}`); + collaborators.sort((a, b) => b.count - a.count); // Remove count after collaborators are sorted From e27e3923ae0a2b6a41c69a2d6ade6479eec407f6 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 15:09:44 +0100 Subject: [PATCH 067/389] update --- src/resources/user/user.service.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index 22bfdd34..ed5f923c 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -2,7 +2,6 @@ import emailGeneratorUtil from '../utilities/emailGenerator.util'; import { UserModel } from './user.model'; import { Data } from '../tool/data.model'; import helper from '../utilities/helper.util'; -import { Cohort } from '../cohort/cohort.model'; import { Collections } from '../collections/collections.model'; import { DataRequestModel } from '../datarequest/datarequest.model'; @@ -88,16 +87,11 @@ export async function setCohortDiscoveryAccess(id, roles) { // Gets all of the logged in users collaborators export const getUsersCollaborators = async (currentUserId) => { let filter = null; - - filter = currentUserId ? { authors: currentUserId } : {}; // Get all collaborators from collections + filter = currentUserId ? { authors: currentUserId } : {}; await getCollaboratorsCollections(filter, currentUserId); - // Get all collaborators from cohorts - // filter = currentUserId ? { uploaders: currentUserId } : {}; - // await getCollaboratorsCohorts(filter, currentUserId); - // Get all collaborators from tools and papers (data collection) filter = currentUserId ? { authors: currentUserId } : {}; // filter = { authors: currentUserId }; @@ -116,11 +110,6 @@ export const getCollaboratorsCollections = async (filter, currentUserId) => { return await populateCollaborators(collaboratorsCollections, 'authors', currentUserId); } -export const getCollaboratorsCohorts = async (filter, currentUserId) => { - let collaboratorsCohorts = await Cohort.find(filter, { _id: 0, uploaders: 1 }).sort({ updatedAt: -1 }); - return await populateCollaborators(collaboratorsCohorts, 'uploaders', currentUserId); -} - export const getCollaboratorsTools = async (filter, currentUserId) => { let collaboratorsTools = await Data.find(filter, { _id: 0, authors: 1 }).sort({ updatedAt: -1 }); return await populateCollaborators(collaboratorsTools, 'authors', currentUserId); From c264c9ccf4e9d787b4462a4b1a31697105f49860 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 15:37:16 +0100 Subject: [PATCH 068/389] remove comments --- src/resources/user/user.service.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index ed5f923c..b65de628 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -86,20 +86,14 @@ export async function setCohortDiscoveryAccess(id, roles) { // Gets all of the logged in users collaborators export const getUsersCollaborators = async (currentUserId) => { - let filter = null; - // Get all collaborators from collections - filter = currentUserId ? { authors: currentUserId } : {}; - await getCollaboratorsCollections(filter, currentUserId); + await getCollaboratorsCollections({ authors: currentUserId }, currentUserId); // Get all collaborators from tools and papers (data collection) - filter = currentUserId ? { authors: currentUserId } : {}; - // filter = { authors: currentUserId }; - await getCollaboratorsTools(filter, currentUserId); + await getCollaboratorsTools({ authors: currentUserId }, currentUserId); // Get all collaborators from DARs - filter = currentUserId ? { $or: [{ userId: currentUserId }, { authorIds: currentUserId }] } : {}; - await getCollaboratorsDARs(filter, currentUserId); + await getCollaboratorsDARs({ $or: [{ userId: currentUserId }, { authorIds: currentUserId }] }, currentUserId); // Strip out duplicate collaborators, add a count return getUniqueCollaborators(arrCollaborators); From 9d0592bed5764a847f62b33f7e7c58952b4e6c18 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 22 Apr 2022 16:46:53 +0100 Subject: [PATCH 069/389] update output data --- src/resources/user/user.service.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/resources/user/user.service.js b/src/resources/user/user.service.js index b65de628..1b13bec3 100644 --- a/src/resources/user/user.service.js +++ b/src/resources/user/user.service.js @@ -299,7 +299,12 @@ export const getUsers = async (currentUserId, filterString = null) => { return collaborator.user; }); - resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + // resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + if (!filterString) { + resolve([...collaboratorUsers]); + } else { + resolve([...collaboratorUsers, ...nonCollaboratorUsers]); + } }); }); } From a73a8f2235c59d30978d1c30df9e649bc85b70ab Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 14 Apr 2022 12:52:33 +0100 Subject: [PATCH 070/389] update message --- src/resources/team/team.controller.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 175ef1c9..531a12c1 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -1,4 +1,4 @@ -import { isEmpty, has, difference, includes, isNull, filter, some } from 'lodash'; +import _, { isEmpty, has, difference, includes, isNull, filter, some } from 'lodash'; import { TeamModel } from './team.model'; import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; @@ -1047,8 +1047,12 @@ const filterMembersByNoticationTypes = (members, notificationTypes) => { */ const filterMembersByNoticationTypesOptIn = (members, notificationTypes) => { return filter(members, member => { + if (!('notifications' in member) || _.isEmpty(member.notifications)) { + return true; + } + return some(member.notifications, notification => { - return includes(notificationTypes, notification.notificationType) && notification.optIn; + return includes(notificationTypes, notification.notificationType) && (notification.optIn === true); }); }); }; From e64131065798cdea539f73815abf1f0b02e1c80a Mon Sep 17 00:00:00 2001 From: kandaj Date: Fri, 29 Apr 2022 15:06:44 +0100 Subject: [PATCH 071/389] send email non production issue fix --- .../__tests__/emailGenerator.util.test.js | 19 +++++++++++++++++++ .../utilities/emailGenerator.util.js | 11 ++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/resources/utilities/__tests__/emailGenerator.util.test.js b/src/resources/utilities/__tests__/emailGenerator.util.test.js index e8a34ada..1cff0fcf 100644 --- a/src/resources/utilities/__tests__/emailGenerator.util.test.js +++ b/src/resources/utilities/__tests__/emailGenerator.util.test.js @@ -22,4 +22,23 @@ describe('Email generator utility functions', () => { expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(false); }); }); + describe('_getRecipients', () => { + const mockRecipients = [ + { email: 'test1@test.com' }, + { email: 'test2@test.com' }, + { email: 'test3@test.com' }, + { email: 'test1@test.com' }, + ]; + it('Should remove duplicaties for production', async () => { + const recipients = emailGenerator.getRecipients(mockRecipients, 'production', 'genericemail@test.com'); + expect(recipients.length).toBe(3); + expect(recipients).toEqual([{ email: 'test1@test.com' }, { email: 'test2@test.com' }, { email: 'test3@test.com' }]); + }); + + it('Should replace recipients non production environtment to generic email', async () => { + const recipients = emailGenerator.getRecipients(mockRecipients, undefined, 'genericemail@test.com'); + expect(recipients.length).toBe(1); + expect(recipients).toEqual([{ email: 'genericemail@test.com' }]); + }); + }); }); diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 4060bf65..fd946a82 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2496,6 +2496,10 @@ ${_displayDataUseRegisterDashboardLink()} return body; }; +const _getRecipients = (recipients, environment, genericEmail) => { + return environment === 'production' ? [...new Map(recipients.map(item => [item['email'], item])).values()] : [{ email: genericEmail }]; +}; + /** * [_sendEmail] * @@ -2507,7 +2511,7 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta sgMail.setApiKey(process.env.SENDGRID_API_KEY); // 2. Ensure any duplicates recieve only a single email - const recipients = [...new Map(to.map(item => [item['email'], item])).values()]; + const recipients = _getRecipients(to, process.env.NODE_ENV, process.env.GENERIC_EMAIL); // 3. Build each email object for SendGrid extracting email addresses from user object with unique unsubscribe link (to) for (let recipient of recipients) { @@ -2522,7 +2526,7 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta // 4. Send email using SendGrid await sgMail.send(msg, false, err => { - if (err && (readEnv === 'test' || readEnv === 'prod')) { + if (err && process.env.NODE_ENV === 'production') { Sentry.addBreadcrumb({ category: 'SendGrid', message: 'Sending email failed', @@ -2545,7 +2549,7 @@ const _sendIntroEmail = msg => { sgMail.setApiKey(process.env.SENDGRID_API_KEY); // 2. Send email using SendGrid sgMail.send(msg, false, err => { - if (err && (readEnv === 'test' || readEnv === 'prod')) { + if (err && process.env.NODE_ENV === 'production') { Sentry.addBreadcrumb({ category: 'SendGrid', message: 'Sending email failed - Intro', @@ -2684,4 +2688,5 @@ export default { generateDataUseRegisterApproved: _generateDataUseRegisterApproved, generateDataUseRegisterRejected: _generateDataUseRegisterRejected, generateDataUseRegisterPending: _generateDataUseRegisterPending, + getRecipients: _getRecipients, }; From 06e4abb9a665091ea4734942a180f72e48d2210c Mon Sep 17 00:00:00 2001 From: Pritesh Bhole Date: Wed, 4 May 2022 13:27:32 +0100 Subject: [PATCH 072/389] changed reject email subject --- 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 e8a32b95..515d21a8 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -955,7 +955,7 @@ const createNotifications = async (type, context) => { emailGenerator.sendEmail( teamMembersDetails, constants.hdrukEmail, - `Your dataset version has been reviewed and rejected`, + `Your federated dataset has been rejected and requires review`, html, false ); From d5e24d2ce501057cd2d74d07f70ff40a3d3a936d Mon Sep 17 00:00:00 2001 From: Pritesh Bhole Date: Wed, 4 May 2022 14:05:43 +0100 Subject: [PATCH 073/389] fixed as per comments --- src/utils/datasetonboarding.util.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 515d21a8..1f431d04 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -952,10 +952,11 @@ const createNotifications = async (type, context) => { comment: context.applicationStatusDesc, }; html = emailGenerator.generateMetadataOnboardingRejected(options); + let subject = (options.isFederated) ? 'Your federated dataset has been rejected and requires review' : 'Your dataset version has been reviewed and rejected' emailGenerator.sendEmail( teamMembersDetails, - constants.hdrukEmail, - `Your federated dataset has been rejected and requires review`, + constants.hdrukEmail, + subject, html, false ); From d3f8c23381802994bf1668a4cbd3a21b3c0bbd2e Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 4 May 2022 16:05:22 +0100 Subject: [PATCH 074/389] hide federation details from publisher model --- src/resources/publisher/publisher.model.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/resources/publisher/publisher.model.js b/src/resources/publisher/publisher.model.js index 6db5672a..8ba20629 100644 --- a/src/resources/publisher/publisher.model.js +++ b/src/resources/publisher/publisher.model.js @@ -44,6 +44,12 @@ const PublisherSchema = new Schema( allowAccessRequestManagement: { type: Boolean, default: false }, uses5Safes: { type: Boolean, default: false }, wordTemplate: String, + federation: { + active: { type: Boolean }, + auth: { type: Object, select: false }, + endpoints: { type: Boolean, select: false }, + notificationEmail: { type: Array, select: false }, + }, }, { toJSON: { virtuals: true }, From 473b60ef6c55325bb28abac45f5ca310be892f19 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 4 May 2022 19:27:29 +0100 Subject: [PATCH 075/389] update notifications for DAR and Workflow partially --- .../datarequest/datarequest.controller.js | 155 +++++++++++++++--- src/resources/team/team.controller.js | 34 +++- 2 files changed, 164 insertions(+), 25 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index e776dc3e..a83ff5ec 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -1947,6 +1947,7 @@ export default class DataRequestController extends Controller { let { firstname, lastname } = user; // Instantiate default params let custodianManagers = [], + custodianManagersIds = [], custodianUserIds = [], managerUserIds = [], emailRecipients = [], @@ -1990,8 +1991,15 @@ export default class DataRequestController extends Controller { switch (type) { case constants.notificationTypes.INPROGRESS: + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagersIds = custodianManagers.map(user => user.id); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } await notificationBuilder.triggerNotificationMessage( - [user.id], + [user.id, ...custodianManagersIds], `An email with the data access request info for ${datasetTitles} has been sent to you`, 'data access request', accessRecord._id @@ -2010,7 +2018,7 @@ export default class DataRequestController extends Controller { // Build email template ({ html } = await emailGenerator.generateEmail(aboutApplication, questions, pages, questionPanels, questionAnswers, options)); await emailGenerator.sendEmail( - [user], + [user, ...custodianManagers], constants.hdrukEmail, `Data Access Request in progress for ${projectName || datasetTitles}`, html, @@ -2022,7 +2030,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, constants.roleTypes.MANAGER); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); let activeStep = this.workflowService.getActiveWorkflowStep(workflow); stepReviewers = this.workflowService.getStepReviewers(activeStep); // Create custodian notification @@ -2033,6 +2041,11 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } // Create applicant notification await notificationBuilder.triggerNotificationMessage( @@ -2084,7 +2097,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, constants.roleTypes.MANAGER); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); // check if publisher.team has email notifications custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( @@ -2094,6 +2107,28 @@ export default class DataRequestController extends Controller { accessRecord._id, accessRecord.datasets[0].publisher._id.toString() ); + if (accessRecord.datasets[0].publisher.team.notifications[0].optIn) { + accessRecord.datasets[0].publisher.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + } else if (_.has(accessRecord, 'publisherObj') && accessRecord.publisherObj.allowAccessRequestManagement) { + // Retrieve all custodian user Ids to generate notifications + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + // check if publisher.team has email notifications + custodianUserIds = custodianManagers.map(user => user.id); + await notificationBuilder.triggerNotificationMessage( + custodianUserIds, + `A Data Access Request has been submitted to ${publisher} for ${projectName || datasetTitles} by ${appFirstName} ${appLastName}`, + 'data access request received', + accessRecord._id, + accessRecord.datasets[0].publisher._id.toString() + ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } } else { const dataCustodianEmail = process.env.DATA_CUSTODIAN_EMAIL || contactPoint; custodianManagers = [{ email: dataCustodianEmail }]; @@ -2185,7 +2220,22 @@ 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, constants.roleTypes.MANAGER); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); + custodianUserIds = custodianManagers.map(user => user.id); + await notificationBuilder.triggerNotificationMessage( + custodianUserIds, + `A Data Access Request has been resubmitted with updates to ${publisher} for ${projectName || datasetTitles} by ${appFirstName} ${appLastName}`, + 'data access request', + accessRecord._id + ); + if (accessRecord.datasets[0].publisher.team.notifications[0].optIn) { + accessRecord.datasets[0].publisher.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + } else if (_.has(accessRecord, 'publisherObj') && accessRecord.publisherObj.allowAccessRequestManagement) { + // Retrieve all custodian user Ids to generate notifications + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2193,6 +2243,11 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } } else { const dataCustodianEmail = process.env.DATA_CUSTODIAN_EMAIL || contactPoint; custodianManagers = [{ email: dataCustodianEmail }]; @@ -2393,16 +2448,21 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.FINALDECISIONREQUIRED: // 1. Get managers for publisher - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleTypes.MANAGER); - managerUserIds = custodianManagers.map(user => user.id); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagersIds = custodianManagers.map(user => user.id); // 2. Create manager notifications notificationBuilder.triggerNotificationMessage( - managerUserIds, + custodianManagersIds, `Action is required as a Data Access Request application for ${publisher} is now awaiting a final decision`, 'data access request', accessRecord._id ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } // 3. Create manager emails options = { id: accessRecord._id, @@ -2425,9 +2485,17 @@ export default class DataRequestController extends Controller { ); break; case constants.notificationTypes.DEADLINEWARNING: + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagersIds = custodianManagers.map(user => user.id); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + // 1. Create reviewer notifications await notificationBuilder.triggerNotificationMessage( - remainingReviewerUserIds, + [...remainingReviewerUserIds,...custodianManagersIds], `The deadline is approaching for a Data Access Request application you are reviewing`, 'data access request', accessRecord._id @@ -2450,7 +2518,7 @@ export default class DataRequestController extends Controller { }; html = await emailGenerator.generateReviewDeadlineWarning(options); await emailGenerator.sendEmail( - remainingReviewers, + [...remainingReviewers, ...custodianManagers], constants.hdrukEmail, `The deadline is approaching for a Data Access Request application you are reviewing`, html, @@ -2459,10 +2527,10 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.DEADLINEPASSED: // 1. Get all managers - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleTypes.MANAGER); - managerUserIds = custodianManagers.map(user => user.id); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianManagersIds = custodianManagers.map(user => user.id); // 2. Combine managers and reviewers remaining - let deadlinePassedUserIds = [...remainingReviewerUserIds, ...managerUserIds]; + let deadlinePassedUserIds = [...remainingReviewerUserIds, ...custodianManagersIds]; let deadlinePassedUsers = [...remainingReviewers, ...custodianManagers]; // 3. Create notifications @@ -2472,6 +2540,12 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + // 4. Create emails options = { id: accessRecord._id, @@ -2499,9 +2573,9 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.WORKFLOWASSIGNED: // 1. Get managers for publisher - custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team.toObject(), constants.roleTypes.MANAGER); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team.toObject(), 'All'); // 2. Get managerIds for notifications - managerUserIds = custodianManagers.map(user => user.id); + custodianManagersIds = custodianManagers.map(user => user.id); // 3. deconstruct and set options for notifications and email options = { id: accessRecord._id, @@ -2516,11 +2590,16 @@ export default class DataRequestController extends Controller { }; // 4. Create notifications for the managers only await notificationBuilder.triggerNotificationMessage( - managerUserIds, + custodianManagersIds, `Workflow of ${workflowName} has been assiged to an appplication`, 'data access request', accessRecord._id ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } // 5. Generate the email html = await emailGenerator.generateWorkflowAssigned(options); // 6. Send email to custodian managers only within the team @@ -2627,7 +2706,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, constants.roleTypes.MANAGER); + custodianManagers = teamController.getTeamMembersByRole(accessRecord.datasets[0].publisher.team, 'All'); custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2635,6 +2714,21 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); + } else if (_.has(accessRecord, 'publisherObj')) { + // Retrieve all custodian user Ids to generate notifications + custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianUserIds = custodianManagers.map(user => user.id); + await notificationBuilder.triggerNotificationMessage( + custodianUserIds, + `An amendment request has been submitted to ${projectId} by ${appFirstName} ${appLastName}`, + 'data access request', + accessRecord._id + ); + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } } else { const dataCustodianEmail = process.env.DATA_CUSTODIAN_EMAIL || contactPoint; custodianManagers = [{ email: dataCustodianEmail }]; @@ -2712,13 +2806,17 @@ export default class DataRequestController extends Controller { break; case constants.notificationTypes.MESSAGESENT: if (userType === constants.userTypes.APPLICANT) { - const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleTypes.MANAGER); + const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); const custodianManagersIds = custodianManagers.map(user => user.id); - const custodianReviewers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, constants.roleTypes.REVIEWER); - const custodianReviewersIds = custodianManagers.map(user => user.id); + + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } await notificationBuilder.triggerNotificationMessage( - [...custodianManagersIds, ...custodianReviewersIds, ...accessRecord.authors.map(author => author.id)], + [...custodianManagersIds, ...accessRecord.authors.map(author => author.id)], `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname}`, 'data access message sent', accessRecord._id @@ -2735,15 +2833,24 @@ export default class DataRequestController extends Controller { }); await emailGenerator.sendEmail( - [...custodianManagers, ...custodianReviewers, ...accessRecord.authors], + [...custodianManagers, ...accessRecord.authors], constants.hdrukEmail, `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname}`, html, false ); } else if (userType === constants.userTypes.CUSTODIAN) { + const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + const custodianManagersIds = custodianManagers.map(user => user.id); + + if (accessRecord.publisherObj.team.notifications[0].optIn) { + accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + await notificationBuilder.triggerNotificationMessage( - [accessRecord.userId, ...accessRecord.authors.map(author => author.id)], + [accessRecord.userId, ...accessRecord.authors.map(author => author.id), ...custodianManagersIds], `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname} from ${accessRecord.publisherObj.name}`, 'data access message sent', accessRecord._id @@ -2760,7 +2867,7 @@ export default class DataRequestController extends Controller { }); await emailGenerator.sendEmail( - [accessRecord.mainApplicant, ...accessRecord.authors], + [accessRecord.mainApplicant, ...accessRecord.authors, ...custodianManagers], constants.hdrukEmail, `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname}`, html, diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 531a12c1..2ef42da6 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -965,10 +965,41 @@ const checkIfAdmin = (user, adminRoles) => { }; const getTeamMembersByRole = (team, role) => { + let { members = [], users = [] } = team; + + let userIds = members.filter(mem => { + if (mem.roles.includes(role) || role === 'All') { + if(!_.has(mem, 'notifications')) { + return true; + } + + if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { + return true; + } + } + }).map(mem => mem.memberid.toString()); + + return users.filter(user => userIds.includes(user._id.toString())); +}; + + +const getTeamMembersNotifications = (team, filterArray) => { // Destructure members array and populated users array (populate 'users' must be included in the original Mongo query) let { members = [], users = [] } = team; // Get all userIds for role within team - let userIds = members.filter(mem => mem.roles.includes(role) || role === 'All').map(mem => mem.memberid.toString()); + let userIds = members.filter(mem => { + if(filterArray.includes(mem.memberid)) { + return false + } + + if(!_.has(mem, 'notifications')) { + return true; + } + + if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { + return true; + } + }).map(mem => mem.memberid.toString()); // return all user records for role return users.filter(user => userIds.includes(user._id.toString())); }; @@ -1293,6 +1324,7 @@ export default { deleteTeamMember: deleteTeamMember, checkTeamPermissions: checkTeamPermissions, getTeamMembersByRole: getTeamMembersByRole, + getTeamMembersNotifications: getTeamMembersNotifications, createNotifications: createNotifications, getTeamsList: getTeamsList, addTeam: addTeam, From b299722bc9f14c9d695b3ecf7e2c784f655aea82 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 9 May 2022 11:58:30 +0100 Subject: [PATCH 076/389] code updated for update and delete workflow --- src/resources/team/team.controller.js | 29 ++----- src/resources/utilities/constants.util.js | 2 + src/resources/workflow/workflow.controller.js | 83 +++++++++++++++---- src/resources/workflow/workflow.service.js | 62 ++++++++++---- 4 files changed, 123 insertions(+), 53 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 2ef42da6..c5a3e643 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -968,10 +968,16 @@ const getTeamMembersByRole = (team, role) => { let { members = [], users = [] } = team; let userIds = members.filter(mem => { + if (mem.roles.includes(role) || role === 'All') { + console.log(`mem getTeamMembersByRole 1 : ${JSON.stringify(mem)}\n`); if(!_.has(mem, 'notifications')) { return true; } + + if (_.has(mem, 'notifications') && !mem.notifications.length) { + return true; + } if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { return true; @@ -982,28 +988,6 @@ const getTeamMembersByRole = (team, role) => { return users.filter(user => userIds.includes(user._id.toString())); }; - -const getTeamMembersNotifications = (team, filterArray) => { - // Destructure members array and populated users array (populate 'users' must be included in the original Mongo query) - let { members = [], users = [] } = team; - // Get all userIds for role within team - let userIds = members.filter(mem => { - if(filterArray.includes(mem.memberid)) { - return false - } - - if(!_.has(mem, 'notifications')) { - return true; - } - - if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { - return true; - } - }).map(mem => mem.memberid.toString()); - // return all user records for role - return users.filter(user => userIds.includes(user._id.toString())); -}; - /** * Extract the name of a team from MongoDb object * @@ -1324,7 +1308,6 @@ export default { deleteTeamMember: deleteTeamMember, checkTeamPermissions: checkTeamPermissions, getTeamMembersByRole: getTeamMembersByRole, - getTeamMembersNotifications: getTeamMembersNotifications, createNotifications: createNotifications, getTeamsList: getTeamsList, addTeam: addTeam, diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index ecf9c574..0b775d52 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -152,6 +152,8 @@ const _notificationTypes = { MEMBERROLECHANGED: 'MemberRoleChanged', WORKFLOWASSIGNED: 'WorkflowAssigned', WORKFLOWCREATED: 'WorkflowCreated', + WORKFLOWUPDATED: 'WorkflowUpdated', + WORKFLOWDELETED: 'WorkflowDeleted', INPROGRESS: 'InProgress', APPLICATIONCLONED: 'ApplicationCloned', APPLICATIONDELETED: 'ApplicationDeleted', diff --git a/src/resources/workflow/workflow.controller.js b/src/resources/workflow/workflow.controller.js index f479a4c3..a48b6b7c 100644 --- a/src/resources/workflow/workflow.controller.js +++ b/src/resources/workflow/workflow.controller.js @@ -168,7 +168,7 @@ export default class WorkflowController extends Controller { async updateWorkflow(req, res) { try { - const { _id: userId } = req.user; + const { _id: userId, firstname, lastname } = req.user; const { id: workflowId } = req.params; // 1. Look up workflow let workflow = await WorkflowModel.findOne({ @@ -202,7 +202,7 @@ export default class WorkflowController extends Controller { }); } // 5. Edit workflow - const { workflowName = '', steps = [] } = req.body; + const { workflowName = '', publisher = '', steps = [] } = req.body; let isDirty = false; // Check if workflow name updated if (!_.isEmpty(workflowName)) { @@ -214,21 +214,45 @@ export default class WorkflowController extends Controller { isDirty = true; } // Perform save if changes have been made if (isDirty) { - workflow.save(async err => { + workflow = await workflow.save().catch(err => { if (err) { - console.error(err.message); return res.status(400).json({ success: false, message: err.message, }); - } else { - // 7. Return workflow payload - return res.status(204).json({ - success: true, - workflow, - }); } }); + + const publisherObj = await PublisherModel.findOne({ + _id: publisher, + }).populate({ + path: 'team members', + populate: { + path: 'users', + select: '_id id email firstname lastname', + }, + }); + if (!publisherObj) { + return res.status(400).json({ + success: false, + message: 'You must supply a valid publisher to create the workflow against', + }); + } + const detailedWorkflow = await WorkflowModel.findById(workflow._id).populate({ + path: 'steps.reviewers', + select: 'firstname lastname email -_id', + }).lean(); + let context = { + publisherObj: publisherObj.team.toObject(), + actioner: `${firstname} ${lastname}`, + workflow: detailedWorkflow, + }; + this.workflowService.createNotifications(context, constants.notificationTypes.WORKFLOWUPDATED); + + return res.status(204).json({ + success: true, + workflow, + }); } else { return res.status(200).json({ success: true, @@ -245,7 +269,7 @@ export default class WorkflowController extends Controller { async deleteWorkflow(req, res) { try { - const { _id: userId } = req.user; + const { _id: userId, firstname, lastname } = req.user; const { id: workflowId } = req.params; // 1. Look up workflow const workflow = await WorkflowModel.findOne({ @@ -258,6 +282,8 @@ export default class WorkflowController extends Controller { select: 'members -_id', }, }); + const { workflowName = '', publisher = {}, steps = [] } = workflow; + if (!workflow) { return res.status(404).json({ success: false }); } @@ -278,6 +304,11 @@ export default class WorkflowController extends Controller { message: 'A workflow which is attached to applications currently in review cannot be deleted', }); } + const detailedWorkflow = await WorkflowModel.findById(workflowId).populate({ + path: 'steps.reviewers', + select: 'firstname lastname email -_id', + }).lean(); + // 5. Delete workflow WorkflowModel.deleteOne({ _id: workflowId }, function (err) { if (err) { @@ -286,13 +317,33 @@ export default class WorkflowController extends Controller { success: false, message: 'An error occurred deleting the workflow', }); - } else { - // 7. Return workflow payload - return res.status(204).json({ - success: true, - }); } }); + const publisherObj = await PublisherModel.findOne({ + _id: publisher._id, + }).populate({ + path: 'team members', + populate: { + path: 'users', + select: '_id id email firstname lastname', + }, + }); + if (!publisherObj) { + return res.status(400).json({ + success: false, + message: 'You must supply a valid publisher to create the workflow against', + }); + } + let context = { + publisherObj: publisherObj.team.toObject(), + actioner: `${firstname} ${lastname}`, + workflow: detailedWorkflow, + }; + this.workflowService.createNotifications(context, constants.notificationTypes.WORKFLOWDELETED); + + return res.status(204).json({ + success: true, + }); } catch (err) { console.error(err.message); return res.status(500).json({ diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 036b224b..4a242ddd 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -162,23 +162,27 @@ export default class WorkflowService { // deconstruct context let { publisherObj, workflow = {}, actioner = '' } = context; + console.log(`publisherObj createNotifications : ${JSON.stringify(publisherObj)}\n`); + custodianManagers = teamController.getTeamMembersByRole(publisherObj, 'All'); + if (publisherObj.notifications[0].optIn) { + publisherObj.notifications[0].subscribedEmails.map(teamEmail => { + custodianManagers.push({email: teamEmail}); + }); + } + managerUserIds = custodianManagers.map(user => user.id); + let { workflowName = 'Workflow Title', _id, steps, createdAt } = workflow; + options = { + actioner, + workflowName, + _id, + steps, + createdAt, + }; + // switch over types switch (type) { case constants.notificationTypes.WORKFLOWCREATED: // 1. Get managers for publisher - custodianManagers = teamController.getTeamMembersByRole(publisherObj, constants.roleTypes.MANAGER); - // 2. Get managerIds for notifications - managerUserIds = custodianManagers.map(user => user.id); - // 3. deconstruct workflow - let { workflowName = 'Workflow Title', _id, steps, createdAt } = workflow; - // 4. setup options - options = { - actioner, - workflowName, - _id, - steps, - createdAt, - }; // 4. Create notifications for the managers only await notificationBuilder.triggerNotificationMessage( managerUserIds, @@ -191,7 +195,37 @@ export default class WorkflowService { // 6. Send email to custodian managers only within the team await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been created`, html, false); break; - } + + case constants.notificationTypes.WORKFLOWUPDATED: + // 1. Get managers for publisher + // 4. Create notifications for the managers only + await notificationBuilder.triggerNotificationMessage( + managerUserIds, + `A new workflow of ${workflowName} has been updated`, + 'workflow', + _id + ); + // 5. Generate the email + html = await emailGenerator.generateWorkflowCreated(options); + // 6. Send email to custodian managers only within the team + await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been updated`, html, false); + break; + + case constants.notificationTypes.WORKFLOWDELETED: + // 1. Get managers for publisher + // 4. Create notifications for the managers only + await notificationBuilder.triggerNotificationMessage( + managerUserIds, + `A new workflow of ${workflowName} has been deleted`, + 'workflow', + _id + ); + // 5. Generate the email + html = await emailGenerator.generateWorkflowCreated(options); + // 6. Send email to custodian managers only within the team + await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been deleted`, html, false); + break; + } } } From 7268e9c25acd3c5196df738a07c5feebda2f7426 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 9 May 2022 14:09:01 +0100 Subject: [PATCH 077/389] removed console.log --- src/resources/team/team.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index c5a3e643..b046623a 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -968,9 +968,7 @@ const getTeamMembersByRole = (team, role) => { let { members = [], users = [] } = team; let userIds = members.filter(mem => { - if (mem.roles.includes(role) || role === 'All') { - console.log(`mem getTeamMembersByRole 1 : ${JSON.stringify(mem)}\n`); if(!_.has(mem, 'notifications')) { return true; } From a064565410a1240d1205257c80b6b6c4594f786f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 9 May 2022 14:09:25 +0100 Subject: [PATCH 078/389] removed console.log --- src/resources/workflow/workflow.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 4a242ddd..c0b4a6ef 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -162,7 +162,6 @@ export default class WorkflowService { // deconstruct context let { publisherObj, workflow = {}, actioner = '' } = context; - console.log(`publisherObj createNotifications : ${JSON.stringify(publisherObj)}\n`); custodianManagers = teamController.getTeamMembersByRole(publisherObj, 'All'); if (publisherObj.notifications[0].optIn) { publisherObj.notifications[0].subscribedEmails.map(teamEmail => { From 8f8739d661cc0d867653aee7e1e94f4d49696a4e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 9 May 2022 16:32:24 +0100 Subject: [PATCH 079/389] update for cohort test --- .../__tests__/getCollaboratorsCohorts.test.js | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/resources/user/__tests__/getCollaboratorsCohorts.test.js diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js deleted file mode 100644 index bb626688..00000000 --- a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import dbHandler from '../../../config/in-memory-db'; -import {mockCohorts} from '../__mocks__/cohorts.data'; - -const {getCollaboratorsCohorts} = require('../user.service'); - - -beforeAll(async () => { - await dbHandler.connect(); - await dbHandler.loadData({ cohorts: mockCohorts }); -}); - -afterAll(async () => { - await dbHandler.clearDatabase(); - await dbHandler.closeDatabase(); -}); - -describe('getCollaboratorsCohorts tests', () => { - it('should return values', async () => { - const currentUserId = 8470291714590257; - const filter = currentUserId ? { uploaders: currentUserId } : {}; - - const result = await getCollaboratorsCohorts(filter, currentUserId); - expect(result.length > 0).toBe(true); - expect(typeof result).toBe('object'); - }); - - it('should return values', async () => { - const currentUserId = null; - const filter = currentUserId ? { uploaders: currentUserId } : {}; - - const result = await getCollaboratorsCohorts(filter, currentUserId); - expect(result.length > 0).toBe(true); - expect(typeof result).toBe('object'); - }); -}); \ No newline at end of file From 8e24a1376d6d4db6d16ce8768344da24d2257bdf Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 12 May 2022 10:41:55 +0100 Subject: [PATCH 080/389] added for non-production mailtrap --- package.json | 1 + .../utilities/emailGenerator.util.js | 119 +++++++++++++----- 2 files changed, 92 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index ff15fcf1..6fcaae1a 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "mongoose": "^5.12.7", "morgan": "^1.10.0", "multer": "^1.4.2", + "nodemailer": "^6.7.5", "oidc-provider": "^6.29.3", "passport": "^0.4.1", "passport-azure-ad-oauth2": "0.0.4", diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index fd946a82..7c032157 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -1,3 +1,4 @@ +require('dotenv').config(); import _, { isNil, isEmpty, capitalize, groupBy, forEach, isEqual } from 'lodash'; import moment from 'moment'; import { UserModel } from '../user/user.model'; @@ -7,12 +8,27 @@ import * as Sentry from '@sentry/node'; import wordTemplateBuilder from '../utilities/wordTemplateBuilder.util'; const fs = require('fs'); +const nodemailer = require('nodemailer'); const sgMail = require('@sendgrid/mail'); -const readEnv = process.env.ENV || 'prod'; +const readEnv = process.env.ENV || 'production'; + let parent, qsId; let questionList = []; let excludedQuestionSetIds = ['addRepeatableSection', 'removeRepeatableSection']; let autoCompleteLookups = { fullname: ['email'] }; +let transporterOptions = { + host: process.env.MAIL_HOST, + port: process.env.MAIL_PORT, + auth: { + user: process.env.MAIL_USERNAME, + pass: process.env.MAIL_PASSWORD, + }, + pool: true, + maxConnections: 1, + rateDelta: 20000, + rateLimit: 5, +}; +let transporter = nodemailer.createTransport(transporterOptions); const _getStepReviewers = (reviewers = []) => { if (!isEmpty(reviewers)) return [...reviewers].map(reviewer => `${reviewer.firstname} ${reviewer.lastname}`).join(', '); @@ -2508,15 +2524,17 @@ const _getRecipients = (recipients, environment, genericEmail) => { */ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, attachments = []) => { // 1. Apply SendGrid API key from environment variable - sgMail.setApiKey(process.env.SENDGRID_API_KEY); + if (process.env.NODE_ENV === 'production') { + sgMail.setApiKey(process.env.SENDGRID_API_KEY); + } // 2. Ensure any duplicates recieve only a single email - const recipients = _getRecipients(to, process.env.NODE_ENV, process.env.GENERIC_EMAIL); - + // const recipients = _getRecipients(to, process.env.NODE_ENV, process.env.GENERIC_EMAIL); + const recipients = [...new Map(to.map(item => [item['email'], item])).values()]; // 3. Build each email object for SendGrid extracting email addresses from user object with unique unsubscribe link (to) for (let recipient of recipients) { let body = _generateEmailHeader + html + _generateEmailFooter(recipient, allowUnsubscribe); - let msg = { + let message = { to: recipient.email, from: from, subject: subject, @@ -2524,20 +2542,71 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta attachments, }; - // 4. Send email using SendGrid - await sgMail.send(msg, false, err => { - if (err && process.env.NODE_ENV === 'production') { - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(err); - } - }); + // 4. Send email + if (process.env.NODE_ENV !== 'production') { + 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); + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(error); + } + } else { + await sgMail.send(message, false, err => { + if (err && process.env.NODE_ENV === 'production') { + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(err); + } + }); + } } }; +const _sendEmailSmtp = async (message) => { + if (process.env.NODE_ENV !== 'production') { + 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); + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(error); + } + } else { + await sgMail.send(message, false, err => { + if (err && process.env.NODE_ENV === 'production') { + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(err); + } + }); + } +} + /** * [_sendIntroEmail] * @@ -2546,18 +2615,12 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta */ const _sendIntroEmail = msg => { // 1. Apply SendGrid API key from environment variable - sgMail.setApiKey(process.env.SENDGRID_API_KEY); - // 2. Send email using SendGrid - sgMail.send(msg, false, err => { - if (err && process.env.NODE_ENV === 'production') { - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed - Intro', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(err); - } - }); + if (process.env.NODE_ENV === 'production') { + sgMail.setApiKey(process.env.SENDGRID_API_KEY); + } + + // 2. Send email + _sendEmailSmtp(msg); }; const _generateEmailHeader = ` From 052b8a232d37f76b268832f4b51ce1658e8f9246 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 12 May 2022 11:05:47 +0100 Subject: [PATCH 081/389] update - using smtp for all envs --- .../utilities/emailGenerator.util.js | 79 +++++-------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 7c032157..651c130a 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2512,10 +2512,6 @@ ${_displayDataUseRegisterDashboardLink()} return body; }; -const _getRecipients = (recipients, environment, genericEmail) => { - return environment === 'production' ? [...new Map(recipients.map(item => [item['email'], item])).values()] : [{ email: genericEmail }]; -}; - /** * [_sendEmail] * @@ -2523,13 +2519,6 @@ const _getRecipients = (recipients, environment, genericEmail) => { * @param {Object} context */ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, attachments = []) => { - // 1. Apply SendGrid API key from environment variable - if (process.env.NODE_ENV === 'production') { - sgMail.setApiKey(process.env.SENDGRID_API_KEY); - } - - // 2. Ensure any duplicates recieve only a single email - // const recipients = _getRecipients(to, process.env.NODE_ENV, process.env.GENERIC_EMAIL); const recipients = [...new Map(to.map(item => [item['email'], item])).values()]; // 3. Build each email object for SendGrid extracting email addresses from user object with unique unsubscribe link (to) for (let recipient of recipients) { @@ -2543,40 +2532,6 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta }; // 4. Send email - if (process.env.NODE_ENV !== 'production') { - 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); - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(error); - } - } else { - await sgMail.send(message, false, err => { - if (err && process.env.NODE_ENV === 'production') { - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(err); - } - }); - } - } -}; - -const _sendEmailSmtp = async (message) => { - if (process.env.NODE_ENV !== 'production') { try { await transporter.sendMail(message, (error, info) => { if (error) { @@ -2593,17 +2548,26 @@ const _sendEmailSmtp = async (message) => { }); Sentry.captureException(error); } - } else { - await sgMail.send(message, false, err => { - if (err && process.env.NODE_ENV === 'production') { - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(err); + + } +}; + +const _sendEmailSmtp = async (message) => { + 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); + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, }); + Sentry.captureException(error); } } @@ -2614,12 +2578,6 @@ const _sendEmailSmtp = async (message) => { * @param {Object} message to from, templateId */ const _sendIntroEmail = msg => { - // 1. Apply SendGrid API key from environment variable - if (process.env.NODE_ENV === 'production') { - sgMail.setApiKey(process.env.SENDGRID_API_KEY); - } - - // 2. Send email _sendEmailSmtp(msg); }; @@ -2751,5 +2709,4 @@ export default { generateDataUseRegisterApproved: _generateDataUseRegisterApproved, generateDataUseRegisterRejected: _generateDataUseRegisterRejected, generateDataUseRegisterPending: _generateDataUseRegisterPending, - getRecipients: _getRecipients, }; From c284ff712edc6a7864896a6ef13bbe037c4186d1 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 12 May 2022 11:13:21 +0100 Subject: [PATCH 082/389] update - using smtp for all envs --- src/resources/utilities/emailGenerator.util.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 651c130a..0ad3a044 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -9,7 +9,6 @@ import wordTemplateBuilder from '../utilities/wordTemplateBuilder.util'; const fs = require('fs'); const nodemailer = require('nodemailer'); -const sgMail = require('@sendgrid/mail'); const readEnv = process.env.ENV || 'production'; let parent, qsId; From 357bfba33a6b21997f425b29a293f70526969818 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 16 May 2022 12:05:47 +0100 Subject: [PATCH 083/389] send custom notification to creator --- src/resources/message/message.controller.js | 19 +++++++++- .../utilities/emailGenerator.util.js | 37 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index a230e18a..e1a82093 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -104,7 +104,7 @@ module.exports = { // 15. Prepare to send email if a new message has been created if (messageType === 'message') { - let optIn, subscribedEmails; + 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 } }); @@ -125,9 +125,12 @@ module.exports = { ); if (!_.isEmpty(subscribedMembersByType)) { // build cleaner array of memberIds from subscribedMembersByType - const memberIds = [...subscribedMembersByType.map(m => m.memberid.toString()), topicObj.createdBy.toString()]; + 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 @@ -155,6 +158,18 @@ module.exports = { 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 + ); + } } // 19. Return successful response with message data const messageObj = message.toObject(); diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 0ad3a044..ecc44db9 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2262,6 +2262,42 @@ const _generateMessageNotification = options => { return body; }; +const _generateMessageCreatorNotification = options => { + let { firstMessage, firstname, lastname, messageDescription, openMessagesLink } = options; + + let body = `
+
+
Thank you for submitting ${name}, which has been reviewed by the team at HDR UK. The dataset version cannot be approved for release on the Gateway at this time. Please look at the comment from the reviewer below and make any necessary changes on a new version of the dataset before resubmitting.
+ + + + + + + + + + +
+ Data Access Enquiry submitted +
+

Dear ${firstname} ${lastname},

+

Thank you for submitting an enquiry about ${firstMessage.datasetsRequested[0].name}.

+

Your enquiry has been sent to ${firstMessage.datasetsRequested[0].publisher} who will reply in due course. If you have not received a response after 10 working days, or if you have any queries or concerns about the Gateway, please email enquiries@hdruk.ac.uk and a member of the HDR UK team will get in touch with you.

+

${messageDescription.replace(/\n/g, '
')}

+
+
+ `; + return body; +}; + const _generateEntityNotification = options => { let { resourceType, resourceName, resourceLink, subject, rejectionReason, activeflag, type, resourceAuthor } = options; let authorBody; @@ -2700,6 +2736,7 @@ export default { //generateMetadataOnboardingUnArchived: _generateMetadataOnboardingUnArchived, //Messages generateMessageNotification: _generateMessageNotification, + generateMessageCreatorNotification: _generateMessageCreatorNotification, generateEntityNotification: _generateEntityNotification, //ActivityLog generateActivityLogManualEventCreated: _generateActivityLogManualEventCreated, From c8df3dbfe645e28ad83b221887912471b1479195 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 16 May 2022 15:39:10 +0100 Subject: [PATCH 084/389] Unpublished updates --- .../datarequest/schema/datarequest.schemas.model.js | 6 ++++++ src/resources/questionbank/questionbank.service.js | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/src/resources/datarequest/schema/datarequest.schemas.model.js b/src/resources/datarequest/schema/datarequest.schemas.model.js index 01bc0d50..4c11c9f8 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.model.js +++ b/src/resources/datarequest/schema/datarequest.schemas.model.js @@ -40,6 +40,12 @@ const DataRequestSchemas = new Schema( type: Number, default: 0, }, + unpublishedGuidance: [ + { + type: String, + default: [], + }, + ], }, { timestamps: true, diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 3e0d7f81..dc34c7c2 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -33,6 +33,7 @@ export default class QuestionbankService { questionStatus, guidance: {}, countOfChanges: 0, + unpublishedGuidance: [], }; const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); @@ -43,6 +44,7 @@ export default class QuestionbankService { guidance: schema.guidance, countOfChanges: schema.countOfChanges, schemaId: schema._id, + unpublishedGuidance: schema.unpublishedGuidance, }; } @@ -61,6 +63,7 @@ export default class QuestionbankService { guidance: latestSchemaVersion.guidance, countOfChanges: latestSchemaVersion.countOfChanges, schemaId: latestSchemaVersion._id, + unpublishedGuidance: latestSchemaVersion.unpublishedGuidance, }; } @@ -79,6 +82,7 @@ export default class QuestionbankService { guidance: latestSchemaVersion.guidance, version: latestSchemaVersion.version + 1, countOfChanges: 0, + unpublishedGuidance: [], }; const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); @@ -89,6 +93,7 @@ export default class QuestionbankService { guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, schemaId: schema._id, + unpublishedGuidance: schema.unpublishedGuidance, }; } else { let questionStatus = {}; @@ -107,6 +112,7 @@ export default class QuestionbankService { guidance: {}, countOfChanges: 0, version: latestSchemaVersion.version + 1, + unpublishedGuidance: [], }; const schema = await this.dataRequestRepository.createApplicationFormSchema(newSchema); @@ -117,6 +123,7 @@ export default class QuestionbankService { guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, schemaId: schema._id, + unpublishedGuidance: newSchema.unpublishedGuidance, }; } } From 03aa6d30a412e7907184ae36247367e7d5165e84 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 16 May 2022 15:47:02 +0100 Subject: [PATCH 085/389] Change to schema --- .../datarequest/schema/datarequest.schemas.model.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/resources/datarequest/schema/datarequest.schemas.model.js b/src/resources/datarequest/schema/datarequest.schemas.model.js index 4c11c9f8..b3a65f01 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.model.js +++ b/src/resources/datarequest/schema/datarequest.schemas.model.js @@ -40,12 +40,10 @@ const DataRequestSchemas = new Schema( type: Number, default: 0, }, - unpublishedGuidance: [ - { - type: String, - default: [], - }, - ], + unpublishedGuidance: { + type: Array, + default: [], + }, }, { timestamps: true, From 252a75d1746c6c60c79ec570adffdaa431c1bb7d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 17 May 2022 12:14:50 +0100 Subject: [PATCH 086/389] update notifications for second message --- src/resources/message/message.controller.js | 25 +++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index e1a82093..823360f2 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -125,13 +125,24 @@ module.exports = { ); if (!_.isEmpty(subscribedMembersByType)) { // build cleaner array of memberIds from subscribedMembersByType - 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]; + console.log(`topicObj : ${JSON.stringify(topicObj)}`); + console.log(`topicObj.createdBy : ${JSON.stringify(topicObj.createdBy)}`); + console.log(`topicObj.createdBy 2 : ${JSON.stringify(typeof topicObj.createdBy === 'object')}`); + 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]; From 559c1bc1939739582f51e2f6e1c90b6f3253d827 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 17 May 2022 12:15:09 +0100 Subject: [PATCH 087/389] update notifications for second message --- src/resources/message/message.controller.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 823360f2..9ef6f337 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -125,9 +125,6 @@ module.exports = { ); if (!_.isEmpty(subscribedMembersByType)) { // build cleaner array of memberIds from subscribedMembersByType - console.log(`topicObj : ${JSON.stringify(topicObj)}`); - console.log(`topicObj.createdBy : ${JSON.stringify(topicObj.createdBy)}`); - console.log(`topicObj.createdBy 2 : ${JSON.stringify(typeof topicObj.createdBy === 'object')}`); 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 From 461dbeba1a1e5b2cb2ad2ec8dbdb0f325f5a1797 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 17 May 2022 13:36:22 +0100 Subject: [PATCH 088/389] update send for email team --- .../dataUseRegister/dataUseRegister.controller.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index f2074714..4ce28683 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -6,6 +6,7 @@ import constants from './../utilities/constants.util'; import { Data } from '../tool/data.model'; import { Course } from '../course/course.model'; import { TeamModel } from '../team/team.model'; +import { PublisherModel } from '../publisher/publisher.model'; import teamController from '../team/team.controller'; import emailGenerator from '../utilities/emailGenerator.util'; import { getObjectFilters } from '../search/search.repository'; @@ -371,13 +372,20 @@ export default class DataUseRegisterController extends Controller { switch (type) { case constants.dataUseRegisterNotifications.DATAUSEAPPROVED: { + let teamEmailNotification = []; const adminTeam = await TeamModel.findOne({ type: 'admin' }) .populate({ path: 'users', }) .lean(); + const team = await TeamModel.findById(dataUseRegister.publisher.toString()); + if (team.notifications.length > 0 && team.notifications[0].optIn) { + team.notifications[0].subscribedEmails.map(teamEmail => { + teamEmailNotification.push({email: teamEmail}); + }); + } const dataUseTeamMembers = teamController.getTeamMembersByRole(adminTeam, constants.roleTypes.ADMIN_DATA_USE); - const emailRecipients = [...dataUseTeamMembers, uploader]; + const emailRecipients = [...dataUseTeamMembers, uploader, ...teamEmailNotification]; const options = { id, From 211d3440de8e712c7107780f76d5ec7a8d1c4a5e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 17 May 2022 13:43:06 +0100 Subject: [PATCH 089/389] removed not used lines --- src/resources/dataUseRegister/dataUseRegister.controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index 4ce28683..87c52a6e 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -6,7 +6,6 @@ import constants from './../utilities/constants.util'; import { Data } from '../tool/data.model'; import { Course } from '../course/course.model'; import { TeamModel } from '../team/team.model'; -import { PublisherModel } from '../publisher/publisher.model'; import teamController from '../team/team.controller'; import emailGenerator from '../utilities/emailGenerator.util'; import { getObjectFilters } from '../search/search.repository'; From 79c1307ea5605efa9e8876ba8444b3ed51a7ad74 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Tue, 17 May 2022 17:50:44 +0100 Subject: [PATCH 090/389] fix error when subbmitting dar to federated custodians --- src/resources/datarequest/datarequest.controller.js | 4 ++-- src/resources/publisher/publisher.model.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index a83ff5ec..052927c7 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -2177,8 +2177,8 @@ export default class DataRequestController extends Controller { options )); // Get the name of the publishers word template - let publisherTemplate = await PublisherModel.findOne({ name: publisher }, { wordTemplate: 1, _id: 0 }).lean(); - let templateName = publisherTemplate.wordTemplate; + let publisherTemplate = await PublisherModel.findOne({ name: publisher }, { _id: 0 }).lean(); + let templateName = publisherTemplate.wordTemplate; // Send emails to custodian team members who have opted in to email notifications if (emailRecipientType === 'dataCustodian') { emailRecipients = [...custodianManagers]; diff --git a/src/resources/publisher/publisher.model.js b/src/resources/publisher/publisher.model.js index 8ba20629..4dbe68c7 100644 --- a/src/resources/publisher/publisher.model.js +++ b/src/resources/publisher/publisher.model.js @@ -47,7 +47,7 @@ const PublisherSchema = new Schema( federation: { active: { type: Boolean }, auth: { type: Object, select: false }, - endpoints: { type: Boolean, select: false }, + endpoints: { type: Object, select: false }, notificationEmail: { type: Array, select: false }, }, }, From 17958fc3baa898cd4b5be3b9fd382bbf7ed0e15f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 20 May 2022 12:09:45 +0100 Subject: [PATCH 091/389] add publish pubsub service --- package.json | 3 +- src/services/google/PubSubService.js | 30 ++++++++++++ .../google/__test__/PubSubService.test.js | 47 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/services/google/PubSubService.js create mode 100644 src/services/google/__test__/PubSubService.test.js diff --git a/package.json b/package.json index 6fcaae1a..592bea93 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dependencies": { "@google-cloud/bigquery": "^5.9.3", "@google-cloud/monitoring": "^2.1.0", + "@google-cloud/pubsub": "^2.19.4", "@google-cloud/storage": "^5.3.0", "@hubspot/api-client": "^4.1.0", "@sendgrid/mail": "^7.1.0", @@ -49,7 +50,7 @@ "lodash": "^4.17.19", "mailchimp-api-v3": "^1.15.0", "migrate-mongoose": "^4.0.0", - "moment": "^2.27.0", + "moment": "^2.29.3", "mongoose": "^5.12.7", "morgan": "^1.10.0", "multer": "^1.4.2", diff --git a/src/services/google/PubSubService.js b/src/services/google/PubSubService.js new file mode 100644 index 00000000..61bbcfc7 --- /dev/null +++ b/src/services/google/PubSubService.js @@ -0,0 +1,30 @@ +const {PubSub} = require('@google-cloud/pubsub'); + +const pubSubClient = new PubSub({ + projectId: process.env.PUBSUB_PROJECT_ID, +}); + +export const pushMessage = async (topicName, message) => { + + const dataBuffer = Buffer.from(JSON.stringify(message)); + + // try { + // const messageId = pubSubClient + // .topic(topicName) + // .publish({data: dataBuffer}); + // console.log(`Message ${messageId} published.`); + // } catch (error) { + // console.error(error); + // throw new Error(`Received error while publishing a message to PubSub`); + // } + + try { + const messageId = pubSubClient + .topic(topicName) + .publishMessage({data: dataBuffer}); + console.log(`Message ${messageId} published.`); + } catch (error) { + console.error(error); + throw new Error(`Received error while publishing a message to PubSub`); + } +}; \ No newline at end of file diff --git a/src/services/google/__test__/PubSubService.test.js b/src/services/google/__test__/PubSubService.test.js new file mode 100644 index 00000000..3931d751 --- /dev/null +++ b/src/services/google/__test__/PubSubService.test.js @@ -0,0 +1,47 @@ +const sinon = require('sinon'); + +const topicName = 'topic-name'; +import { pushMessage } from '../PubSubService'; + +jest.mock('@google-cloud/pubsub', () => ({ + __esModule: true, + PubSub: jest.fn().mockImplementation(() => ({ + topic: jest.fn(), + publishMessage: jest.fn().mockImplementation(() => { + return { + get: jest.fn(), + publish: jest.fn(), + publishMessage: jest.fn() + }; + }), + })), +})) + +const stubConsole = function () { + sinon.stub(console, 'error'); + sinon.stub(console, 'log'); +}; + +const restoreConsole = function () { + console.log.restore(); + console.error.restore(); +}; + +beforeEach(stubConsole); +afterEach(restoreConsole); + +describe('PubSub', () => { + it(`publish message function was called`, async () => { + const mockFn = jest.fn().mockName("pushMessage"); + mockFn(); // comment me + expect(mockFn).toHaveBeenCalled(); + }); + + it(`publish message failed`, async () => { + const message = { + foo: 'bar', + }; + + expect(async () => await pushMessage(topicName, message)).rejects.toThrow(); + }); +}); \ No newline at end of file From 7a917d397385a61d8afe60c481fada77e432d829 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 23 May 2022 13:36:11 +0300 Subject: [PATCH 092/389] json to pubsub --- src/resources/message/message.controller.js | 26 ++++++++++++- .../__tests__/emailGenerator.util.test.js | 38 +++++++++---------- src/services/google/PubSubService.js | 16 +------- .../google/__test__/PubSubService.test.js | 6 +-- 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 9ef6f337..23b39448 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -9,6 +9,7 @@ import { Data as ToolModel } from '../tool/data.model'; import constants from '../utilities/constants.util'; import { dataRequestService } from '../datarequest/dependency'; import { activityLogService } from '../activitylog/dependency'; +import { publishMessageToPubSub } from '../../services/google/PubSubService'; const topicController = require('../topic/topic.controller'); @@ -20,10 +21,11 @@ module.exports = { let { messageType = 'message', topic = '', messageDescription, relatedObjectIds, firstMessage } = req.body; let topicObj = {}; let team, publisher, userType; + let tools = {}; // 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 - const tools = await ToolModel.find() + tools = await ToolModel.find() .where('_id') .in(relatedObjectIds) .populate({ @@ -36,7 +38,8 @@ module.exports = { }, }, }); - // 3. Return undefined if no object(s) exists + + // 3. Return undefined if no object(s) exists if (_.isEmpty(tools)) return undefined; // 4. Get recipients for new message @@ -87,6 +90,7 @@ module.exports = { } } } + // 13. Create new message const message = await MessagesModel.create({ messageID: parseInt(Math.random().toString().replace('0.', '')), @@ -99,6 +103,7 @@ module.exports = { readBy: [new mongoose.Types.ObjectId(createdBy)], ...(userType && { userType }), }); + // 14. Return 500 error if message was not successfully created if (!message) return res.status(500).json({ success: false, message: 'Could not save message to database.' }); @@ -178,6 +183,23 @@ module.exports = { false ); } + + // publish the message to PubSub + const pubSubMessage = { + id: "", + publisherInfo: { + id: tools[0].publisher._id, + name: tools[0].publisher.name, + }, + data: { + topicId: topicObj._id, + messageId: message.messageID, + createdDate: message.createdDate, + data: req.body.firstMessage, + + } + }; + publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, pubSubMessage); } // 19. Return successful response with message data const messageObj = message.toObject(); diff --git a/src/resources/utilities/__tests__/emailGenerator.util.test.js b/src/resources/utilities/__tests__/emailGenerator.util.test.js index 1cff0fcf..b80a9a43 100644 --- a/src/resources/utilities/__tests__/emailGenerator.util.test.js +++ b/src/resources/utilities/__tests__/emailGenerator.util.test.js @@ -22,23 +22,23 @@ describe('Email generator utility functions', () => { expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(false); }); }); - describe('_getRecipients', () => { - const mockRecipients = [ - { email: 'test1@test.com' }, - { email: 'test2@test.com' }, - { email: 'test3@test.com' }, - { email: 'test1@test.com' }, - ]; - it('Should remove duplicaties for production', async () => { - const recipients = emailGenerator.getRecipients(mockRecipients, 'production', 'genericemail@test.com'); - expect(recipients.length).toBe(3); - expect(recipients).toEqual([{ email: 'test1@test.com' }, { email: 'test2@test.com' }, { email: 'test3@test.com' }]); - }); - - it('Should replace recipients non production environtment to generic email', async () => { - const recipients = emailGenerator.getRecipients(mockRecipients, undefined, 'genericemail@test.com'); - expect(recipients.length).toBe(1); - expect(recipients).toEqual([{ email: 'genericemail@test.com' }]); - }); - }); + // describe('_getRecipients', () => { + // const mockRecipients = [ + // { email: 'test1@test.com' }, + // { email: 'test2@test.com' }, + // { email: 'test3@test.com' }, + // { email: 'test1@test.com' }, + // ]; + // it('Should remove duplicaties for production', async () => { + // const recipients = emailGenerator.getRecipients(mockRecipients, 'production', 'genericemail@test.com'); + // expect(recipients.length).toBe(3); + // expect(recipients).toEqual([{ email: 'test1@test.com' }, { email: 'test2@test.com' }, { email: 'test3@test.com' }]); + // }); + + // it('Should replace recipients non production environtment to generic email', async () => { + // const recipients = emailGenerator.getRecipients(mockRecipients, undefined, 'genericemail@test.com'); + // expect(recipients.length).toBe(1); + // expect(recipients).toEqual([{ email: 'genericemail@test.com' }]); + // }); + // }); }); diff --git a/src/services/google/PubSubService.js b/src/services/google/PubSubService.js index 61bbcfc7..02459410 100644 --- a/src/services/google/PubSubService.js +++ b/src/services/google/PubSubService.js @@ -4,24 +4,12 @@ const pubSubClient = new PubSub({ projectId: process.env.PUBSUB_PROJECT_ID, }); -export const pushMessage = async (topicName, message) => { +export const publishMessageToPubSub = async (topicName, message) => { const dataBuffer = Buffer.from(JSON.stringify(message)); - // try { - // const messageId = pubSubClient - // .topic(topicName) - // .publish({data: dataBuffer}); - // console.log(`Message ${messageId} published.`); - // } catch (error) { - // console.error(error); - // throw new Error(`Received error while publishing a message to PubSub`); - // } - try { - const messageId = pubSubClient - .topic(topicName) - .publishMessage({data: dataBuffer}); + const messageId = pubSubClient.topic(topicName).publishMessage({data: dataBuffer}); console.log(`Message ${messageId} published.`); } catch (error) { console.error(error); diff --git a/src/services/google/__test__/PubSubService.test.js b/src/services/google/__test__/PubSubService.test.js index 3931d751..a4b820ad 100644 --- a/src/services/google/__test__/PubSubService.test.js +++ b/src/services/google/__test__/PubSubService.test.js @@ -1,7 +1,7 @@ const sinon = require('sinon'); const topicName = 'topic-name'; -import { pushMessage } from '../PubSubService'; +import { publishMessageToPubSub } from '../PubSubService'; jest.mock('@google-cloud/pubsub', () => ({ __esModule: true, @@ -32,7 +32,7 @@ afterEach(restoreConsole); describe('PubSub', () => { it(`publish message function was called`, async () => { - const mockFn = jest.fn().mockName("pushMessage"); + const mockFn = jest.fn().mockName("publishMessageToPubSub"); mockFn(); // comment me expect(mockFn).toHaveBeenCalled(); }); @@ -42,6 +42,6 @@ describe('PubSub', () => { foo: 'bar', }; - expect(async () => await pushMessage(topicName, message)).rejects.toThrow(); + expect(async () => await publishMessageToPubSub(topicName, message)).rejects.toThrow(); }); }); \ No newline at end of file From 3e121848aac513601c041ddba3f249b634fdc024 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Tue, 24 May 2022 10:57:33 +0100 Subject: [PATCH 093/389] Initial commit --- .../datarequest/datarequest.controller.js | 184 +++++++++++------- 1 file changed, 119 insertions(+), 65 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index e776dc3e..96f3effe 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -21,7 +21,15 @@ const logCategory = 'Data Access Request'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); export default class DataRequestController extends Controller { - constructor(dataRequestService, workflowService, amendmentService, topicService, messageService, activityLogService, dataUseRegisterService) { + constructor( + dataRequestService, + workflowService, + amendmentService, + topicService, + messageService, + activityLogService, + dataUseRegisterService + ) { super(dataRequestService); this.dataRequestService = dataRequestService; this.workflowService = workflowService; @@ -29,7 +37,7 @@ export default class DataRequestController extends Controller { this.activityLogService = activityLogService; this.topicService = topicService; this.messageService = messageService; - this.dataUseRegisterService = dataUseRegisterService + this.dataUseRegisterService = dataUseRegisterService; } // ###### APPLICATION CRUD OPERATIONS ####### @@ -329,7 +337,6 @@ export default class DataRequestController extends Controller { //Get additional info to pre populate for user and collaborators (authors) let contributors = await this.dataRequestService.getDarContributors(darId, userId); - // Return payload return res.status(200).json({ success: true, @@ -698,23 +705,22 @@ export default class DataRequestController extends Controller { if (accessRecord.applicationStatus === constants.applicationStatuses.APPROVED) { const dataUseRegister = await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); - await dataUseRegisterController.createNotifications( - constants.dataUseRegisterNotifications.DATAUSEAPPROVED, - {}, - dataUseRegister - ); + await dataUseRegisterController.createNotifications( + constants.dataUseRegisterNotifications.DATAUSEAPPROVED, + {}, + dataUseRegister + ); await this.activityLogService.logActivity(constants.activityLogEvents.data_access_request.APPLICATION_APPROVED, { accessRequest: accessRecord, user: req.user, }); - } - else if (accessRecord.applicationStatus === constants.applicationStatuses.APPROVEDWITHCONDITIONS) { + } else if (accessRecord.applicationStatus === constants.applicationStatuses.APPROVEDWITHCONDITIONS) { const dataUseRegister = await this.dataUseRegisterService.createDataUseRegister(requestingUser, accessRecord); - await dataUseRegisterController.createNotifications( - constants.dataUseRegisterNotifications.DATAUSEAPPROVED, - {}, - dataUseRegister - ); + await dataUseRegisterController.createNotifications( + constants.dataUseRegisterNotifications.DATAUSEAPPROVED, + {}, + dataUseRegister + ); await this.activityLogService.logActivity( constants.activityLogEvents.data_access_request.APPLICATION_APPROVED_WITH_CONDITIONS, { @@ -2029,7 +2035,9 @@ export default class DataRequestController extends Controller { let statusChangeUserIds = [...custodianManagers, ...stepReviewers].map(user => user.id); await notificationBuilder.triggerNotificationMessage( statusChangeUserIds, - `${appFirstName} ${appLastName}'s Data Access Request for ${projectName || datasetTitles} was ${context.applicationStatus} by ${firstname} ${lastname}`, + `${appFirstName} ${appLastName}'s Data Access Request for ${projectName || datasetTitles} was ${ + context.applicationStatus + } by ${firstname} ${lastname}`, 'data access request', accessRecord._id ); @@ -2046,7 +2054,9 @@ export default class DataRequestController extends Controller { if (!_.isEmpty(authors)) { await notificationBuilder.triggerNotificationMessage( authors.map(author => author.id), - `A Data Access Request you are contributing to for ${projectName || datasetTitles} was ${context.applicationStatus} by ${publisher}`, + `A Data Access Request you are contributing to for ${projectName || datasetTitles} was ${ + context.applicationStatus + } by ${publisher}`, 'data access request', accessRecord._id ); @@ -2089,7 +2099,9 @@ export default class DataRequestController extends Controller { custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, - `A Data Access Request has been submitted to ${publisher} for ${projectName || datasetTitles} by ${appFirstName} ${appLastName}`, + `A Data Access Request has been submitted to ${publisher} for ${ + projectName || datasetTitles + } by ${appFirstName} ${appLastName}`, 'data access request received', accessRecord._id, accessRecord.datasets[0].publisher._id.toString() @@ -2109,7 +2121,9 @@ export default class DataRequestController extends Controller { if (!_.isEmpty(authors)) { await notificationBuilder.triggerNotificationMessage( accessRecord.authors.map(author => author.id), - `A Data Access Request you are contributing to for ${projectName || datasetTitles} was successfully submitted to ${publisher} by ${firstname} ${lastname}`, + `A Data Access Request you are contributing to for ${ + projectName || datasetTitles + } was successfully submitted to ${publisher} by ${firstname} ${lastname}`, 'data access request', accessRecord._id ); @@ -2142,8 +2156,8 @@ export default class DataRequestController extends Controller { options )); // Get the name of the publishers word template - let publisherTemplate = await PublisherModel.findOne({ name: publisher }, { wordTemplate: 1, _id: 0 }).lean(); - let templateName = publisherTemplate.wordTemplate; + let publisherTemplate = await PublisherModel.findOne({ name: publisher }, { wordTemplate: 1, _id: 0 }).lean(); + let templateName = publisherTemplate.wordTemplate; // Send emails to custodian team members who have opted in to email notifications if (emailRecipientType === 'dataCustodian') { emailRecipients = [...custodianManagers]; @@ -2177,7 +2191,9 @@ export default class DataRequestController extends Controller { } // Remove temporary files for word attachment - if (!_.isUndefined(templateName)) { await emailGenerator.deleteWordAttachmentTempFiles() } + if (!_.isUndefined(templateName)) { + await emailGenerator.deleteWordAttachmentTempFiles(); + } } break; case constants.notificationTypes.RESUBMITTED: @@ -2189,7 +2205,9 @@ export default class DataRequestController extends Controller { custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, - `A Data Access Request has been resubmitted with updates to ${publisher} for ${projectName || datasetTitles} by ${appFirstName} ${appLastName}`, + `A Data Access Request has been resubmitted with updates to ${publisher} for ${ + projectName || datasetTitles + } by ${appFirstName} ${appLastName}`, 'data access request', accessRecord._id ); @@ -2208,7 +2226,9 @@ export default class DataRequestController extends Controller { if (!_.isEmpty(authors)) { await notificationBuilder.triggerNotificationMessage( accessRecord.authors.map(author => author.id), - `A Data Access Request you are contributing to for ${projectName || datasetTitles} was successfully resubmitted with updates to ${publisher} by ${firstname} ${lastname}`, + `A Data Access Request you are contributing to for ${ + projectName || datasetTitles + } was successfully resubmitted with updates to ${publisher} by ${firstname} ${lastname}`, 'data access request', accessRecord._id ); @@ -2551,7 +2571,9 @@ export default class DataRequestController extends Controller { if (!_.isEmpty(authors)) { await notificationBuilder.triggerNotificationMessage( authors.map(author => author.id), - `A Data Access Request you contributed to for ${projectName || datasetTitles} has been duplicated into a new form by ${firstname} ${lastname}`, + `A Data Access Request you contributed to for ${ + projectName || datasetTitles + } has been duplicated into a new form by ${firstname} ${lastname}`, 'data access request unlinked', newApplicationId ); @@ -2593,7 +2615,9 @@ export default class DataRequestController extends Controller { if (!_.isEmpty(authors)) { await notificationBuilder.triggerNotificationMessage( authors.map(author => author.id), - `A draft Data Access Request you contributed to for ${projectName || datasetTitles} has been deleted by ${firstname} ${lastname}`, + `A draft Data Access Request you contributed to for ${ + projectName || datasetTitles + } has been deleted by ${firstname} ${lastname}`, 'data access request unlinked', accessRecord._id ); @@ -2744,7 +2768,9 @@ export default class DataRequestController extends Controller { } else if (userType === constants.userTypes.CUSTODIAN) { await notificationBuilder.triggerNotificationMessage( [accessRecord.userId, ...accessRecord.authors.map(author => author.id)], - `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname} from ${accessRecord.publisherObj.name}`, + `There is a new message for the application ${projectName || datasetTitles} from ${user.firstname} ${user.lastname} from ${ + accessRecord.publisherObj.name + }`, 'data access message sent', accessRecord._id ); @@ -2817,7 +2843,7 @@ export default class DataRequestController extends Controller { try { const { params: { id }, - query: { messageType, questionId }, + query: { messageType, questionId, panelId }, } = req; const requestingUserId = parseInt(req.user.id); const requestingUserObjectId = req.user._id; @@ -2846,7 +2872,9 @@ export default class DataRequestController extends Controller { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } - const topic = await this.topicService.getTopicForDAR(id, questionId, messageType); + const topic = await this.topicService.getTopicForDAR(id, questionId || panelId, messageType); + + console.log('*** TOPIC', topic); let messages = []; if (!_.isEmpty(topic) && !_.isEmpty(topic.topicMessages)) { @@ -2875,25 +2903,30 @@ export default class DataRequestController extends Controller { //POST api/v1/data-access-request/:id/messages async submitMessage(req, res) { + console.log('*** submitting message', req.body); + try { const { params: { id }, } = req; - const { questionId, messageType, messageBody } = req.body; + const { questionId, panel, messageType, messageBody } = req.body; const requestingUserId = parseInt(req.user.id); const requestingUserObjectId = req.user._id; const requestingUser = req.user; + console.log('*** accessRecord'); + 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.' }); } - + console.log('*** permissions'); const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, requestingUserObjectId ); + if (!authorised) { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } else if ( @@ -2907,13 +2940,15 @@ export default class DataRequestController extends Controller { ) { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } - - let topic = await this.topicService.getTopicForDAR(id, questionId, messageType); + console.log('*** topics'); + let topic = await this.topicService.getTopicForDAR(id, questionId || panel.panelId, messageType); if (_.isEmpty(topic)) { - topic = await this.topicService.createTopicForDAR(id, questionId, messageType); + topic = await this.topicService.createTopicForDAR(id, questionId || panel.panelId, messageType); } + console.log('*** creating message', topic); + await this.messageService.createMessageForDAR(messageBody, topic._id, requestingUserObjectId, userType); if (messageType === constants.DARMessageTypes.DARMESSAGE) { @@ -2921,43 +2956,62 @@ export default class DataRequestController extends Controller { foundQuestionSet = {}, foundPage = {}; - for (let questionSet of accessRecord.jsonSchema.questionSets) { - foundQuestion = datarequestUtil.findQuestion(questionSet.questions, questionId); - if (foundQuestion) { - foundQuestionSet = questionSet; - break; + if (questionId) { + for (let questionSet of accessRecord.jsonSchema.questionSets) { + foundQuestion = datarequestUtil.findQuestion(questionSet.questions, questionId); + if (foundQuestion) { + foundQuestionSet = questionSet; + break; + } } - } - - const panel = dynamicForm.findQuestionPanel(foundQuestionSet.questionSetId, accessRecord.jsonSchema.questionPanels); + console.log('*** finding panel'); + const foundPanel = dynamicForm.findQuestionPanel(foundQuestionSet.questionSetId, accessRecord.jsonSchema.questionPanels); - for (let page of accessRecord.jsonSchema.pages) { - if (page.pageId === panel.pageId) { - foundPage = page; - break; + for (let page of accessRecord.jsonSchema.pages) { + if (page.pageId === foundPanel.pageId) { + foundPage = page; + break; + } } - } - const answer = - accessRecord.questionAnswers && accessRecord.questionAnswers[questionId] - ? accessRecord.questionAnswers[questionId] - : 'No answer for this question'; + const answer = + accessRecord.questionAnswers && accessRecord.questionAnswers[questionId] + ? accessRecord.questionAnswers[questionId] + : 'No answer for this question'; - this.createNotifications( - constants.notificationTypes.MESSAGESENT, - { - userType, - messageBody, - questionWithAnswer: { - question: foundQuestion.question, - questionPanel: foundQuestionSet.questionSetHeader, - page: foundPage.title, - answer, + this.createNotifications( + constants.notificationTypes.MESSAGESENT, + { + userType, + messageBody, + questionWithAnswer: { + question: foundQuestion.question, + questionPanel: foundQuestionSet.questionSetHeader, + page: foundPage.title, + answer, + }, }, - }, - accessRecord, - requestingUser - ); + accessRecord, + requestingUser + ); + } else if (panel) { + console.log('*** creating notifications'); + this.createNotifications( + constants.notificationTypes.MESSAGESENT, + { + userType, + messageBody, + questionWithAnswer: { + question: '', + questionPanel: panel.panelId, + page: panel.panelHeader, + answer: '', + }, + }, + accessRecord, + requestingUser + ); + } } this.activityLogService.logActivity( From 169f389b55e2498e0e6a10fb97e659641d25388d Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 24 May 2022 19:09:05 +0300 Subject: [PATCH 094/389] added publish redis pubsub --- package.json | 1 + src/resources/message/message.controller.js | 35 ++++++++++--------- .../__test__/cachePubSubClient.test.js | 23 ++++++++++++ src/services/cachePubSub/cachePubSubClient.js | 14 ++++++++ 4 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 src/services/cachePubSub/__test__/cachePubSubClient.test.js create mode 100644 src/services/cachePubSub/cachePubSubClient.js diff --git a/package.json b/package.json index 592bea93..45bdd404 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "prettier": "^2.2.1", "query-string": "^6.12.1", "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", "snyk": "^1.334.0", diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 23b39448..6ae8b314 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -9,7 +9,7 @@ import { Data as ToolModel } from '../tool/data.model'; import constants from '../utilities/constants.util'; import { dataRequestService } from '../datarequest/dependency'; import { activityLogService } from '../activitylog/dependency'; -import { publishMessageToPubSub } from '../../services/google/PubSubService'; +import { publishMessageToChannel } from '../../services/cachePubSub/cachePubSubClient'; const topicController = require('../topic/topic.controller'); @@ -184,22 +184,25 @@ module.exports = { ); } - // publish the message to PubSub - const pubSubMessage = { - id: "", - publisherInfo: { - id: tools[0].publisher._id, - name: tools[0].publisher.name, - }, - data: { - topicId: topicObj._id, - messageId: message.messageID, - createdDate: message.createdDate, - data: req.body.firstMessage, + // publish the message to Redis PubSub + if (process.env.CACHE_ENABLED) { + const pubSubMessage = { + id: "", + publisherInfo: { + id: tools[0].publisher._id, + name: tools[0].publisher.name, + }, + data: { + topicId: topicObj._id, + messageId: message.messageID, + createdDate: message.createdDate, + data: req.body.firstMessage, + + } + }; + await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + } - } - }; - publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, pubSubMessage); } // 19. Return successful response with message data const messageObj = message.toObject(); diff --git a/src/services/cachePubSub/__test__/cachePubSubClient.test.js b/src/services/cachePubSub/__test__/cachePubSubClient.test.js new file mode 100644 index 00000000..bb0e7332 --- /dev/null +++ b/src/services/cachePubSub/__test__/cachePubSubClient.test.js @@ -0,0 +1,23 @@ +import redis from 'redis'; +import { publishMessageToChannel } from '../cachePubSubClient'; + +jest.mock('redis', () => { + return { + createClient: () => { + return { + createClient: jest.fn(), + connect: jest.fn(), + publish: jest.fn(async () => "123qwe"), + on: jest.fn(() => jest.fn()), + } + } + } +}); + +describe('test redis pubsub client', () => { + it('publish message to channel', async () => { + const channel = 'channel_test'; + const message = 'message_test' + await publishMessageToChannel(channel, message); + }) +}); \ No newline at end of file diff --git a/src/services/cachePubSub/cachePubSubClient.js b/src/services/cachePubSub/cachePubSubClient.js new file mode 100644 index 00000000..615bb322 --- /dev/null +++ b/src/services/cachePubSub/cachePubSubClient.js @@ -0,0 +1,14 @@ +import { createClient } from 'redis'; +const client = createClient({ + url: process.env.CACHE_URL +}); + +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')); + + await client.publish(channel, message); +} \ No newline at end of file From c520fb741dac4a4f3c415901cb692f016b9a8d11 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Wed, 25 May 2022 15:11:46 +0100 Subject: [PATCH 095/389] Remove console log --- .../datarequest/datarequest.controller.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 96f3effe..20e00e5f 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -2874,8 +2874,6 @@ export default class DataRequestController extends Controller { const topic = await this.topicService.getTopicForDAR(id, questionId || panelId, messageType); - console.log('*** TOPIC', topic); - let messages = []; if (!_.isEmpty(topic) && !_.isEmpty(topic.topicMessages)) { for (let topicMessage of topic.topicMessages.reverse()) { @@ -2903,8 +2901,6 @@ export default class DataRequestController extends Controller { //POST api/v1/data-access-request/:id/messages async submitMessage(req, res) { - console.log('*** submitting message', req.body); - try { const { params: { id }, @@ -2914,13 +2910,11 @@ export default class DataRequestController extends Controller { const requestingUserObjectId = req.user._id; const requestingUser = req.user; - console.log('*** accessRecord'); - 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.' }); } - console.log('*** permissions'); + const { authorised, userType } = datarequestUtil.getUserPermissionsForApplication( accessRecord, requestingUserId, @@ -2940,15 +2934,13 @@ export default class DataRequestController extends Controller { ) { return res.status(401).json({ status: 'failure', message: 'Unauthorised' }); } - console.log('*** topics'); + let topic = await this.topicService.getTopicForDAR(id, questionId || panel.panelId, messageType); if (_.isEmpty(topic)) { topic = await this.topicService.createTopicForDAR(id, questionId || panel.panelId, messageType); } - console.log('*** creating message', topic); - await this.messageService.createMessageForDAR(messageBody, topic._id, requestingUserObjectId, userType); if (messageType === constants.DARMessageTypes.DARMESSAGE) { @@ -2964,7 +2956,7 @@ export default class DataRequestController extends Controller { break; } } - console.log('*** finding panel'); + const foundPanel = dynamicForm.findQuestionPanel(foundQuestionSet.questionSetId, accessRecord.jsonSchema.questionPanels); for (let page of accessRecord.jsonSchema.pages) { @@ -2995,7 +2987,6 @@ export default class DataRequestController extends Controller { requestingUser ); } else if (panel) { - console.log('*** creating notifications'); this.createNotifications( constants.notificationTypes.MESSAGESENT, { From 04987295e4d867ebf57ec31ae3db2061aba3307e Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 25 May 2022 18:10:00 +0300 Subject: [PATCH 096/389] update redis pubsub --- src/resources/message/message.controller.js | 10 +++++++--- src/services/google/PubSubService.js | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 6ae8b314..1876fd2f 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -10,8 +10,10 @@ import constants from '../utilities/constants.util'; import { dataRequestService } from '../datarequest/dependency'; import { activityLogService } from '../activitylog/dependency'; import { publishMessageToChannel } from '../../services/cachePubSub/cachePubSubClient'; +import { PublisherModel } from '../publisher/publisher.model'; const topicController = require('../topic/topic.controller'); +const { ObjectId } = require('mongodb'); module.exports = { // POST /api/v1/messages @@ -185,12 +187,14 @@ module.exports = { } // publish the message to Redis PubSub - if (process.env.CACHE_ENABLED) { + let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); + + if (publisherDetails['dar-integration']['enabled']) { const pubSubMessage = { id: "", publisherInfo: { - id: tools[0].publisher._id, - name: tools[0].publisher.name, + id: publisherDetails._id, + name: publisherDetails.name, }, data: { topicId: topicObj._id, diff --git a/src/services/google/PubSubService.js b/src/services/google/PubSubService.js index 02459410..9cd4789f 100644 --- a/src/services/google/PubSubService.js +++ b/src/services/google/PubSubService.js @@ -1,3 +1,8 @@ +// example params in .env file: +// PUBSUB_PROJECT_ID='hdruk-gateway-dev' +// PUBSUB_TOPIC_ENQUIRY='enquiry' +// PUBSUB_SUBSCRIPTION_ID='enquiry-sub' + const {PubSub} = require('@google-cloud/pubsub'); const pubSubClient = new PubSub({ From df2c9fe84bfc5cc4f8df41c51d74ef3d82b1f491 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 27 May 2022 08:14:24 +0100 Subject: [PATCH 097/389] question bank updates clearing --- .../questionbank/questionbank.controller.js | 22 +++++ .../questionbank/questionbank.route.js | 7 ++ .../questionbank/questionbank.service.js | 86 +++++++++++++++++-- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index cd1033f5..27ce2746 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -65,4 +65,26 @@ export default class QuestionbankController extends Controller { }); } } + + async revertChanges(req, res) { + try { + const { publisherId } = req.params; + const { questionSet } = req.query; + + let target; + if (questionSet) { + target = questionSet; + } + + await this.questionbankService.revertChanges(publisherId, target); + + 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', + }); + } + } } diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index e3a44a3a..b9531b1d 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -89,4 +89,11 @@ router.post('/:schemaId', passport.authenticate('jwt'), validatePostRequest, aut questionbankController.publishSchema(req, res) ); +// @route PATCH /api/v2/questionbank/publisherId +// @desc Revert the draft schema to the previous version +// @access Public +router.patch('/:publisherId', passport.authenticate('jwt'), validateViewRequest, authorizeViewRequest, (req, res) => + questionbankController.revertChanges(req, res) +); + module.exports = router; diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index dc34c7c2..1e8283af 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -16,15 +16,8 @@ export default class QuestionbankService { let dataRequestSchemas = await this.dataRequestRepository.getApplicationFormSchemas(publisher); if (isEmpty(dataRequestSchemas)) { - let questionStatus = {}; - //create the questionStatus from the master schema - masterSchema.questionSets.forEach(questionSet => { - questionSet.questions.forEach(question => { - if (question.lockedQuestion === 1) questionStatus[question.questionId] = 2; - else questionStatus[question.questionId] = question.defaultQuestion; - }); - }); + let questionStatus = await this.getDefaultQuestionStates(); const newSchema = { publisher: publisher.name, @@ -200,4 +193,81 @@ export default class QuestionbankService { return newQuestionsAdded; } + + async revertChanges(publisherId, target) { + const publisher = await this.publisherService.getPublisher(publisherId); + const dataRequestSchemas = await this.dataRequestRepository.getApplicationFormSchemas(publisher); + + if (dataRequestSchemas.length === 0) { + throw new Error('This publisher has no data request schemas'); + } + + // Default previous state is the master schema + let previousState = await this.getDefaultQuestionStates(); + + let guidance = {}; + let unpublishedGuidance = []; + // Is previous version exists, previousState is last schema version + if (dataRequestSchemas.length > 1) { + previousState = dataRequestSchemas[1].questionStatus; + guidance = dataRequestSchemas[1].guidance || {}; + } + + // Revert updates for a given question panel ELSE revert all updates + if (target) { + const panelQuestions = await this.getPanelQuestions(target); + const updates = Object.keys(previousState).filter(key => !panelQuestions.includes(key)); + + updates.forEach(key => { + previousState[key] = dataRequestSchemas[0].questionStatus[key]; + + if (dataRequestSchemas[0].unpublishedGuidance.includes(key)) { + unpublishedGuidance.push(key); + } + + if (Object.keys(dataRequestSchemas[0].guidance).includes(key)) { + guidance[key] = dataRequestSchemas[0].guidance[key]; + } + }); + } + + await this.dataRequestRepository.updateApplicationFormSchemaById(dataRequestSchemas[0]._id, { + questionStatus: previousState, + unpublishedGuidance, + guidance, + }); + + return; + } + + async getDefaultQuestionStates() { + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const masterSchema = global.masterSchema; + + let defaultQuestionStates = {}; + + masterSchema.questionSets.forEach(questionSet => { + questionSet.questions.forEach(question => { + if (question.lockedQuestion === 1) defaultQuestionStates[question.questionId] = 2; + else defaultQuestionStates[question.questionId] = question.defaultQuestion; + }); + }); + + return defaultQuestionStates; + } + + async getPanelQuestions(target) { + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const questionSets = global.masterSchema.questionSets; + + const panelQuestions = questionSets.filter(questionSet => questionSet.questionSetId === target); + + if (panelQuestions.length === 0) { + throw new Error('This is not a valid questionSetId'); + } + + const questionIds = panelQuestions[0].questions.map(question => question.questionId); + + return questionIds; + } } From cc7f1d0e98be776170b8c0572e595f76835f5818 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 31 May 2022 11:50:48 +0300 Subject: [PATCH 098/389] publish message to redis pubsub --- .../datarequest/datarequest.controller.js | 23 +++++++++++++++++++ src/resources/message/message.controller.js | 1 + .../__tests__/emailGenerator.util.test.js | 19 --------------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 052927c7..661319d8 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -16,9 +16,11 @@ import { logger } from '../utilities/logger'; import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; import { dataUseRegisterController } from '../dataUseRegister/dependency'; +import { publishMessageToChannel } from '../../services/cachePubSub/cachePubSubClient'; const logCategory = 'Data Access Request'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); +const { ObjectId } = require('mongodb'); export default class DataRequestController extends Controller { constructor(dataRequestService, workflowService, amendmentService, topicService, messageService, activityLogService, dataUseRegisterService) { @@ -450,6 +452,27 @@ export default class DataRequestController extends Controller { bpmController.postStartPreReview(bpmContext); } + // publish the message to Redis PubSub + let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); + + if (accessRecord.applicationStatus === constants.applicationStatuses.SUBMITTED + && publisherDetails['dar-integration']['enabled']) { + const pubSubMessage = { + id: "", + type: "5safes", + publisherInfo: { + id: accessRecord.publisherObj._id, + name: accessRecord.publisherObj.name, + }, + data: { + dataRequestId: accessRecord._id, + createdDate: accessRecord.createdAt, + data: accessRecord.questionAnswers, + } + }; + await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + } + // 11. Return aplication and successful response return res.status(200).json({ status: 'success', data: savedAccessRecord }); } catch (err) { diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 1876fd2f..1a0933c5 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -192,6 +192,7 @@ module.exports = { if (publisherDetails['dar-integration']['enabled']) { const pubSubMessage = { id: "", + type: "enquiry", publisherInfo: { id: publisherDetails._id, name: publisherDetails.name, diff --git a/src/resources/utilities/__tests__/emailGenerator.util.test.js b/src/resources/utilities/__tests__/emailGenerator.util.test.js index b80a9a43..e8a34ada 100644 --- a/src/resources/utilities/__tests__/emailGenerator.util.test.js +++ b/src/resources/utilities/__tests__/emailGenerator.util.test.js @@ -22,23 +22,4 @@ describe('Email generator utility functions', () => { expect(emailBody.includes('Do not apply these changes directly to the Gateway')).toBe(false); }); }); - // describe('_getRecipients', () => { - // const mockRecipients = [ - // { email: 'test1@test.com' }, - // { email: 'test2@test.com' }, - // { email: 'test3@test.com' }, - // { email: 'test1@test.com' }, - // ]; - // it('Should remove duplicaties for production', async () => { - // const recipients = emailGenerator.getRecipients(mockRecipients, 'production', 'genericemail@test.com'); - // expect(recipients.length).toBe(3); - // expect(recipients).toEqual([{ email: 'test1@test.com' }, { email: 'test2@test.com' }, { email: 'test3@test.com' }]); - // }); - - // it('Should replace recipients non production environtment to generic email', async () => { - // const recipients = emailGenerator.getRecipients(mockRecipients, undefined, 'genericemail@test.com'); - // expect(recipients.length).toBe(1); - // expect(recipients).toEqual([{ email: 'genericemail@test.com' }]); - // }); - // }); }); From fd8b7e00e4082d2f4d1c556607c9d64a29f94d84 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Tue, 31 May 2022 10:36:39 +0100 Subject: [PATCH 099/389] questionbank revert changes updates --- .../activeSchemaCreatedThroughForm.js | 24 +- .../activeSchemaNotCreatedThroughForm.js | 24 +- .../questionbank/__mocks__/masterSchema.json | 6570 +++++++++++------ .../questionbank/__mocks__/questionbank.js | 2 +- .../__tests__/questionbank.service.test.js | 241 +- .../questionbank/questionbank.controller.js | 10 +- .../questionbank/questionbank.service.js | 17 +- .../__tests__/getCollaboratorsCohorts.test.js | 35 - 8 files changed, 4593 insertions(+), 2330 deletions(-) delete mode 100644 src/resources/user/__tests__/getCollaboratorsCohorts.test.js diff --git a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js index ca1cc68c..2ffe4a21 100644 --- a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js @@ -34,19 +34,19 @@ export const expectedSchema = { 'safeoutputs-dataretention-retaindatadate': 0, safeoutputsdataretentionretaindatadatereason: 1, safeoutputsdataretentionretaindataextensionpermissions: 0, - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantfullname: 2, + safepeopleprimaryapplicantjobtitle: 2, + safepeopleprimaryapplicanttelephone: 2, safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, + safepeopleprimaryapplicantemail: 2, + safepeopleotherindividualsfullname: 2, + safepeopleotherindividualsjobtitle: 2, + safepeopleotherindividualsorganisation: 2, + safepeopleotherindividualsrole: 2, + safepeopleotherindividualsaccessdata: 2, + safepeopleotherindividualsaccreditedresearcher: 2, + safepeopleotherindividualstraininginformationgovernance: 2, + safepeopleotherindividualsexperience: 2, }, guidance: { safedatadataflowdiagramenclosed: 'This is the guidance for safedatadataflowdiagramenclosed', diff --git a/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js index 244a634b..38756fb9 100644 --- a/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/activeSchemaNotCreatedThroughForm.js @@ -313,19 +313,19 @@ export const expectedSchema = { 'safeoutputs-dataretention-retaindatadate': 1, safeoutputsdataretentionretaindatadatereason: 1, safeoutputsdataretentionretaindataextensionpermissions: 1, - safepeopleprimaryapplicantfullname: 0, - safepeopleprimaryapplicantjobtitle: 1, - safepeopleprimaryapplicanttelephone: 0, + safepeopleprimaryapplicantfullname: 2, + safepeopleprimaryapplicantjobtitle: 2, + safepeopleprimaryapplicanttelephone: 2, safepeopleprimaryapplicantorcid: 1, - safepeopleprimaryapplicantemail: 0, - safepeopleotherindividualsfullname: 1, - safepeopleotherindividualsjobtitle: 0, - safepeopleotherindividualsorganisation: 0, - safepeopleotherindividualsrole: 0, - safepeopleotherindividualsaccessdata: 1, - safepeopleotherindividualsaccreditedresearcher: 0, - safepeopleotherindividualstraininginformationgovernance: 0, - safepeopleotherindividualsexperience: 1, + safepeopleprimaryapplicantemail: 2, + safepeopleotherindividualsfullname: 2, + safepeopleotherindividualsjobtitle: 2, + safepeopleotherindividualsorganisation: 2, + safepeopleotherindividualsrole: 2, + safepeopleotherindividualsaccessdata: 2, + safepeopleotherindividualsaccreditedresearcher: 2, + safepeopleotherindividualstraininginformationgovernance: 2, + safepeopleotherindividualsexperience: 2, }, guidance: {}, countOfChanges: 0, diff --git a/src/resources/questionbank/__mocks__/masterSchema.json b/src/resources/questionbank/__mocks__/masterSchema.json index 25dc5c34..0ba839c0 100644 --- a/src/resources/questionbank/__mocks__/masterSchema.json +++ b/src/resources/questionbank/__mocks__/masterSchema.json @@ -1,2333 +1,4499 @@ { - "_id": { "$oid": "615f1c4be27d54d9fdc494fb" }, + "_id": { + "$oid": "61547b0a76fa2c2e984579c7" + }, "localeId": "en-gb", - "__v": { "$numberInt": "0" }, - "createdAt": { "$date": { "$numberLong": "1633623114800" } }, + "__v": 0, + "createdAt": { + "$date": { + "$numberLong": "1632926473875" + } + }, "entry": { - "name": "dataUtility", - "items": [ - { - "key": "allowable_uses", - "dimension": "Allowable uses", - "category": "Access & Provision", - "definition": "Allowable dataset usages as per the licencing agreement, following ethical and IG approval", - "includeInWizard": true, - "wizardStepTitle": "Allowable uses", - "wizardStepDescription": "Please select the identifier that is most relevant to you (optional)", - "wizardStepOrder": { "$numberInt": "1" }, - "wizardStepType": "radio", - "entries": [ - { - "id": { "$oid": "615f1c4a70e33243450fdf6a" }, - "displayOrder": { "$numberInt": "1" }, - "label": "Academic research", - "impliedValues": ["platinum", "gold", "silver", "bronze"] - }, - { - "id": { "$oid": "615f1c4a70e3320a980fdf6b" }, - "displayOrder": { "$numberInt": "2" }, - "label": "Student / educational project", - "impliedValues": ["platinum", "silver", "gold", "bronze"] - }, - { - "id": { "$oid": "615f1c4a70e332c6150fdf6c" }, - "displayOrder": { "$numberInt": "3" }, - "label": "Charitable or non-profit project", - "impliedValues": ["platinum", "gold", "silver"] - }, - { - "id": { "$oid": "615f1c4a70e33284ae0fdf6d" }, - "displayOrder": { "$numberInt": "4" }, - "label": "Commercial project", - "impliedValues": ["platinum", "gold"] - } - ] + "name": "dataUtility", + "items": [ + { + "key": "allowable_uses", + "dimension": "Allowable uses", + "category": "Access & Provision", + "definition": "Allowable dataset usages as per the licencing agreement, following ethical and IG approval", + "includeInWizard": true, + "wizardStepTitle": "Allowable uses", + "wizardStepDescription": "Please select the identifier that is most relevant to you (optional)", + "wizardStepOrder": 1, + "wizardStepType": "radio", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83ec5e940ac87" + }, + "displayOrder": 1, + "label": " Available for specific academic research uses only", + "labelAlias": "Academic research", + "impliedValues": [ + "platinum", + "gold", + "silver", + "bronze" + ] }, { - "key": "time_lag", - "dimension": "Time Lag", - "category": "Access & Provision", - "definition": "Lag between the data being collected and added to the dataset", - "includeInWizard": true, - "wizardStepTitle": "Time lag", - "wizardStepDescription": "What is the maximum acceptable time delay from the data being generated to entering the dataset? (optional)", - "wizardStepOrder": { "$numberInt": "2" }, - "wizardStepType": "radio", - "entries": [ - { - "id": { "$oid": "615f1c4a70e3326b190fdf6e" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Effectively real-time data", - "label": "Near real-time data", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332a2c60fdf6f" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Approximately 1 week", - "label": "1 week", - "impliedValues": ["platinum", "gold"] - }, - { - "id": { "$oid": "615f1c4a70e33284c10fdf70" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Approximately 1 month", - "label": "1 month", - "impliedValues": ["platinum", "gold", "silver"] - }, - { - "id": { "$oid": "615f1c4a70e332ed880fdf71" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Approximately 1 year", - "label": "1 year", - "impliedValues": ["platinum", "gold", "silver", "bronze"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e4ad040ac88" + }, + "displayOrder": 2, + "label": "Available for academic and non-profit (e.g. charity, public sector) uses only", + "labelAlias": "Student / educational project", + "impliedValues": [ + "platinum", + "silver", + "gold", + "bronze" + ] }, { - "key": "timeliness", - "dimension": "Timeliness", - "category": "Access & Provision", - "definition": "Average data access request timeframe", - "entries": [ - { - "id": { "$oid": "615f1c4a70e332284b0fdf72" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Less than 2 weeks", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332f1080fdf73" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Less than 1 month", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332c9210fdf74" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Less than 3 months", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33242ec0fdf75" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Less than 6 months", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e320040ac89" + }, + "displayOrder": 3, + "label": "Available for limited commercial uses (e.g. relating to a specific domain), in addition to academic and other noncommercial uses", + "labelAlias": "Charitable or non-profit project", + "impliedValues": [ + "platinum", + "gold", + "silver" + ] }, { - "key": "data_quality_management_process", - "dimension": "Data Quality Management Process", - "category": "Technical Quality", - "definition": "The level of maturity of the data quality management processes ", - "entries": [ - { - "id": { "$oid": "615f1c4a70e332ce2f0fdf76" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Externally verified compliance with the data management plan, e.g. by ISO, CQC, ICO or other body", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332d3810fdf77" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Evidence that the data management plan has been implemented is available", - "impliedValues": ["silver", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33261b60fdf78" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "A documented data management plan covering collection, auditing, and management is available for the dataset", - "impliedValues": ["bronze", "silver", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e03dc40ac8a" + }, + "displayOrder": 4, + "label": "Available for wider commercial uses (in line with ethical and IG approval), and addition to academic and other non-commercial uses", + "labelAlias": "Commercial project", + "impliedValues": [ + "platinum", + "gold" + ] }, { - "key": "pathway_coverage", - "dimension": "Pathway coverage", - "category": "Coverage", - "definition": "Representation of multi-disciplinary healthcare data", - "entries": [ - { - "id": { "$oid": "615f1c4a70e33200ba0fdf79" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Contains data across more than two tiers", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e3320f2b0fdf7a" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Contains multimodal data or data that is linked across two tiers (e.g. primary and secondary care)", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33277bc0fdf7b" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Contains data from multiple specialties or services within a single tier of care", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33236b50fdf7c" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Contains data from a single speciality or area", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e376040ac8b" + }, + "displayOrder": 5, + "label": "N/A", + "impliedValues": [] + } + ] + }, + { + "key": "time_lag", + "dimension": "Time Lag", + "category": "Access & Provision", + "definition": "Lag between the data being collected and added to the dataset", + "includeInWizard": true, + "wizardStepTitle": "Time lag", + "wizardStepDescription": "What is the maximum acceptable time delay from the data being generated to entering the dataset? (optional)", + "wizardStepOrder": 2, + "wizardStepType": "radio", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e1f6640ac8c" + }, + "displayOrder": 1, + "definition": "Effectively real-time data", + "label": "Near real-time data", + "impliedValues": [ + "platinum" + ] }, { - "key": "length_of_follow_up", - "dimension": "Length of follow up", - "category": "Coverage", - "definition": "Average timeframe in which a patient appears in a dataset (follow up period)", - "wizardStepTitle": "Length of follow up", - "wizardStepDescription": "What is the minimum required time frame for patients appearing in dataset, on average? (optional)", - "includeInWizard": true, - "wizardStepOrder": { "$numberInt": "3" }, - "wizardStepType": "radio", - "entries": [ - { - "id": { "$oid": "615f1c4a70e3327fe00fdf7d" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Between 1-6 months", - "label": "Between 1-6 months", - "impliedValues": ["platinum", "gold", "silver", "bronze"] - }, - { - "id": { "$oid": "615f1c4a70e33218480fdf7e" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Between 6-12 months", - "label": "Between 6-12 months", - "impliedValues": ["platinum", "gold", "silver"] - }, - { - "id": { "$oid": "615f1c4a70e33217000fdf7f" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Between 1-10 years", - "label": "Between 1-10 years", - "impliedValues": ["platinum", "gold"] - }, - { - "id": { "$oid": "615f1c4a70e332fb220fdf80" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "More than 10 years", - "label": "More than 10 years", - "impliedValues": ["platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83ebfd540ac8d" + }, + "displayOrder": 2, + "definition": "Approximately 1 week", + "label": "1 week", + "impliedValues": [ + "platinum", + "gold" + ] }, { - "key": "availability_of_additional_documentation_and_support", - "dimension": "Availability of additional documentation and support", - "category": "Data Documentation", - "definition": "Available dataset documentation in addition to the data dictionary", - "entries": [ - { - "id": { "$oid": "615f1c4a70e3326e4a0fdf81" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "As Gold, plus support personnel available to answer questions", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33230810fdf82" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "As Silver, plus dataset publication was supported with a journal article explaining the dataset in detail, or dataset training materials", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33231900fdf83" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Comprehensive ReadMe describing extracting and use of data, Dataset FAQS available, Visual data model provided", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33206280fdf84" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Past journal articles demonstrate that knowledge of the data exists", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e795440ac8e" + }, + "displayOrder": 3, + "definition": "Approximately 1 month", + "label": "1 month", + "impliedValues": [ + "platinum", + "gold", + "silver" + ] }, { - "key": "data_model", - "dimension": "Data Model", - "category": "Data Documentation", - "definition": "Availability of clear, documented data model", - "includeInWizard": true, - "wizardStepTitle": "Data model", - "wizardStepDescription": "What data model requirements do you have? (optional)", - "wizardStepOrder": { "$numberInt": "4" }, - "wizardStepType": "radio", - "entries": [ - { - "id": { "$oid": "615f1c4a70e332cd770fdf85" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Known and accepted data model but some key field un-coded or free text", - "label": "Known and accepted data model, with some key fields uncoded ", - "impliedValues": ["platinum", "gold", "silver", "bronze"] - }, - { - "id": { "$oid": "615f1c4a70e3326ff80fdf86" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Key fields codified using a local standard", - "label": "Key fields coded using local standard", - "impliedValues": ["platinum", "gold", "silver"] - }, - { - "id": { "$oid": "615f1c4a70e332dffc0fdf87" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Key fields codified using a national or international standard", - "label": "Key fields coded using national/international standard", - "impliedValues": ["platinum", "gold"] - }, - { - "id": { "$oid": "615f1c4a70e33265420fdf88" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Data Model conforms to a national standard and key fields codified using a national/international standard", - "label": "Model conforms to national standard and key fields coded to national/internal standard", - "impliedValues": ["platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e5fe840ac8f" + }, + "displayOrder": 4, + "definition": "Approximately 1 year", + "label": "1 year", + "impliedValues": [ + "platinum", + "gold", + "silver", + "bronze" + ] }, { - "key": "data_dictionary", - "dimension": "Data Dictionary", - "category": "Data Documentation", - "definition": "Provided documented data dictionary and terminologies", - "entries": [ - { - "id": { "$oid": "615f1c4a70e3327e410fdf89" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Dictionary is based on international standards and includes mapping", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332c0710fdf8a" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Dictionary relates to national definitions", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332cf5c0fdf8b" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Definitions compiled into local data dictionary which is available online", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33286440fdf8c" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Data definitions available", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e0f0040ac90" + }, + "displayOrder": 5, + "definition": "N/A", + "label": "N/A", + "impliedValues": [] + } + ] + }, + { + "key": "timeliness", + "dimension": "Timeliness", + "category": "Access & Provision", + "definition": "Average data access request timeframe", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e17fd40ac91" + }, + "displayOrder": 1, + "definition": "Less than 2 weeks", + "impliedValues": [ + "platinum" + ] }, { - "key": "provenance", - "dimension": "Provenance", - "category": "Data Documentation", - "definition": "Clear description of source and history of the dataset, providing a \"transparent data pipeline\"", - "includeInWizard": true, - "wizardStepTitle": "Provenance", - "wizardStepDescription": "To what level of detail do you require the origin of the dataset to be documented? (optional)", - "wizardStepOrder": { "$numberInt": "5" }, - "wizardStepType": "radio", - "entries": [ - { - "id": { "$oid": "615f1c4a70e33250010fdf8d" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Source of the dataset is documented", - "label": "Dataset source documented", - "impliedValues": ["platinum", "gold", "silver", "bronze"] - }, - { - "id": { "$oid": "615f1c4a70e33244eb0fdf8e" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "Source of the dataset and any transformations, rules and exclusions documented", - "label": "Dataset source, any transformations, rule and exclusions documented", - "impliedValues": ["platinum", "gold", "silver"] - }, - { - "id": { "$oid": "615f1c4a70e33229a80fdf8f" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "All original data items listed, all transformations, rules and exclusion listed and impact of these", - "label": "All original data items, transformations, rules, exclusions and impact listed", - "impliedValues": ["platinum", "gold"] - }, - { - "id": { "$oid": "615f1c4a70e33231780fdf90" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Ability to view earlier versions, including versions before any transformations have been applied data (in line with deidentification and IG approval) and review the impact of each stage of data cleaning", - "label": "Earlier and 'raw' versions and the impact of each stage of data cleaning", - "impliedValues": ["platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e51db40ac92" + }, + "displayOrder": 2, + "definition": "Less than 1 month", + "impliedValues": [ + "gold" + ] }, { - "includeInWizard": true, - "wizardStepTitle": "Search terms", - "wizardStepDescription": "Please type in any relevant search terms to refine your search (optional)", - "wizardStepOrder": { "$numberInt": "6" }, - "wizardStepType": "search", - "entries": [] - }, - { - "key": "linkages", - "dimension": "Linkages", - "category": "Value & Interest", - "definition": "Ability to link with other datasets", - "entries": [ - { - "id": { "$oid": "615f1c4a70e332ffaf0fdf91" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "Existing linkage with reusable or downstream approvals", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e3323aca0fdf92" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "List of restrictions on the type of linkages detailed. List of previously successful dataset linkages performed, with navigable links to linked datasets via at DOI/URL", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33237280fdf93" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "Available linkages outlined and/or List of datasets previously successfully linked provided", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332e92d0fdf94" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "Identifiers to demonstrate ability to link to other datasets", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e3d3d40ac93" + }, + "displayOrder": 3, + "definition": "Less than 3 months", + "impliedValues": [ + "silver" + ] }, { - "key": "data_enrichments", - "dimension": "Data Enrichments", - "category": "Value & Interest", - "definition": "Data sources enriched with annotations, image labels, phenomes, derivations, NLP derived data labels", - "entries": [ - { - "id": { "$oid": "615f1c4a70e33271010fdf95" }, - "displayOrder": { "$numberInt": "1" }, - "definition": "The data include additional derived fields, or enriched data", - "impliedValues": ["platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33264900fdf96" }, - "displayOrder": { "$numberInt": "2" }, - "definition": "The data include additional derived fields, or enriched data used by other available data sources", - "impliedValues": ["gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e33233050fdf97" }, - "displayOrder": { "$numberInt": "3" }, - "definition": "The derived fields or enriched data were generated from, or used by, a peer reviewed algorithm", - "impliedValues": ["silver", "gold", "platinum"] - }, - { - "id": { "$oid": "615f1c4a70e332ea210fdf98" }, - "displayOrder": { "$numberInt": "4" }, - "definition": "The data includes derived fields or enriched data from a national report", - "impliedValues": ["bronze", "silver", "gold", "platinum"] - } - ] + "id": { + "$oid": "618a8b8ba5c83e7ff940ac94" + }, + "displayOrder": 4, + "definition": "Less than 6 months", + "impliedValues": [ + "bronze" + ] } - ] - }, - "languageCode": "en", - "updatedAt": { "$date": { "$numberLong": "1633623114800" } }, - "masterSchema": { - "pages": [ + ] + }, + { + "key": "data_quality_management_process", + "dimension": "Data Quality Management Process", + "category": "Technical Quality", + "definition": "The level of maturity of the data quality management processes ", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e6e6340ac95" + }, + "displayOrder": 1, + "definition": "Externally verified compliance with the data management plan, e.g. by ISO, CQC, ICO or other body", + "impliedValues": [ + "platinum" + ] + }, { - "active": true, - "description": "Who is going to be accessing the data?\n\nSafe People should have the right motivations for accessing research data and understand the legal and ethical considerations when using data that may be sensitive or confidential. Safe People should also have sufficient skills, knowledge and experience to work with the data effectively. Researchers may need to undergo specific training or accreditation before accessing certain data or research environments and demonstrate that they are part of a bona fide research organisation.\n\nThe purpose of this section is to ensure that:\n- details of people who will be accessing the data and the people who are responsible for completing the application are identified\n- any individual or organisation that intends to access the data requested is identified\n- all identified individuals have the necessary accreditation and/or expertise to work with the data effectively.", - "title": "Safe people", - "pageId": "safepeople" + "id": { + "$oid": "618a8b8ba5c83e50b140ac96" + }, + "displayOrder": 2, + "definition": "Evidence that the data management plan has been implemented is available", + "impliedValues": [ + "silver" + ] }, { - "pageId": "safeproject", - "description": "What is the purpose of accessing the data?\n\nSafe projects are those that have a valid research purpose with a defined public benefit. \nFor access to data to be granted the researchers need to demonstrate that their proposal is an appropriate and ethical use of the data, and that it is intended to deliver clear public benefits. The purpose of this section is to ensure that:\n- the project rationale is explained in lay terms\n- the research purpose has a defined public benefit. This can be new knowledge, new treatments, improved pathways of care, new techniques of training staff. \n- how the data requested will be used to achieve the project objectives is articulated.", - "active": false, - "title": "Safe project" + "id": { + "$oid": "618a8b8ba5c83e868e40ac97" + }, + "displayOrder": 3, + "definition": "A documented data management plan covering collection, auditing, and management is available for the dataset", + "impliedValues": [ + "bronze" + ] + } + ] + }, + { + "key": "pathway_coverage", + "dimension": "Pathway coverage", + "category": "Coverage", + "definition": "Representation of multi-disciplinary healthcare data", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e779140ac98" + }, + "displayOrder": 1, + "definition": "Contains data across more than two tiers", + "impliedValues": [ + "platinum" + ] }, { - "pageId": "safedata", - "description": "Safe data ensure that researchers have a clear legal basis for accessing the data and do not inadvertently learn something about the data subjects during the course of their analysis, minimising the risks of re-identification.\nThe minimisation of this risk could be achieved by removing direct identifiers, aggregating values, banding variables, or other statistical techniques that may make re-identification more difficult. Sensitive or confidential data could not be considered to be completely safe because of the residual risk to a data subject’s confidentiality. Hence other limitations on access will need to be applied.\n\nThe purpose of this section is to ensure that: \n- there is a clear legal basis for accessing the requested data\n- the data requested is proportionate to the requirement of the project \n- all data requested is necessary in order to achieve the public benefit declared \n- data subjects cannot be identified by your team by cross-referencing datasets from anywhere else.", - "active": false, - "title": "Safe data" + "id": { + "$oid": "618a8b8ba5c83ed79640ac99" + }, + "displayOrder": 2, + "definition": "Contains multimodal data or data that is linked across two tiers (e.g. primary and secondary care)", + "impliedValues": [ + "gold" + ] }, { - "active": false, - "description": "Safe settings are analytics environments where researchers can access and analyse the requested datasets in a safe and ethical way. Safe settings encompass the physical environment and procedural arrangements such as the supervision and auditing regimes. For safe settings, the likelihood of both deliberate and accidental disclosure needs to be explicitly considered.\n\nThe purpose of this section is to ensure that:\n\n- researchers access requested data in a secure and controlled setting such as a Trusted Research Environment (TRE) that limits the unauthorised use of the data\n- practical controls and appropriate restrictions are in place if researchers access data though non-TRE environment. There may be requirements that data is held on restricted access servers, encrypted and only decrypted at the point of use.", - "title": "Safe settings", - "pageId": "safesettings" + "id": { + "$oid": "618a8b8ba5c83e526340ac9a" + }, + "displayOrder": 3, + "definition": "Contains data from multiple specialties or services within a single tier of care", + "impliedValues": [ + "silver" + ] }, { - "pageId": "safeoutputs", - "description": "Safe outputs ensure that all research outputs cannot be used to identify data subjects. They typically include ‘descriptive statistics’ that have been sufficiently aggregated such that identification is near enough impossible, and modelled outputs which are inherently non-confidential. The purpose of this section is to ensure that:\n\n- controls are in place to minimise risks associated with planned outputs and publications \n- the researchers aim to openly publish their results to enable use, scrutiny and further research.", - "active": false, - "title": "Safe outputs" + "id": { + "$oid": "618a8b8ba5c83ef8ea40ac9b" + }, + "displayOrder": 4, + "definition": "Contains data from a single speciality or area", + "impliedValues": [ + "bronze" + ] } - ], - "formPanels": [ - { "pageId": "safepeople", "index": { "$numberDouble": "1.0" }, "panelId": "primaryapplicant" }, - { "index": { "$numberDouble": "2.0" }, "pageId": "safepeople", "panelId": "safepeople-otherindividuals" }, - { "index": { "$numberDouble": "3.0" }, "pageId": "safeproject", "panelId": "safeproject-aboutthisapplication" }, - { "pageId": "safeproject", "index": { "$numberDouble": "4.0" }, "panelId": "safeproject-projectdetails" }, - { "panelId": "safeproject-funderinformation", "index": { "$numberDouble": "5.0" }, "pageId": "safeproject" }, - { "panelId": "safeproject-sponsorinformation", "pageId": "safeproject", "index": { "$numberDouble": "6.0" } }, - { "panelId": "safeproject-declarationofinterest", "index": { "$numberDouble": "7.0" }, "pageId": "safeproject" }, - { "pageId": "safeproject", "index": { "$numberDouble": "8.0" }, "panelId": "safeproject-intellectualproperty" }, - { "panelId": "safedata-datafields", "pageId": "safedata", "index": { "$numberDouble": "9.0" } }, - { "pageId": "safedata", "index": { "$numberDouble": "10.0" }, "panelId": "safedata-otherdatasetsintentiontolinkdata" }, - { "panelId": "safedata-lawfulbasis", "index": { "$numberDouble": "11.0" }, "pageId": "safedata" }, - { "pageId": "safedata", "index": { "$numberDouble": "12.0" }, "panelId": "safedata-confidentialityavenue" }, - { "pageId": "safedata", "index": { "$numberDouble": "13.0" }, "panelId": "safedata-ethicalapproval" }, - { "index": { "$numberDouble": "14.0" }, "pageId": "safesettings", "panelId": "safesettings-storageandprocessing" }, - { "pageId": "safesettings", "index": { "$numberDouble": "15.0" }, "panelId": "safesettings-dataflow" }, - { "index": { "$numberDouble": "16.0" }, "pageId": "safeoutputs", "panelId": "safeoutputs-outputsdisseminationplans" }, - { "pageId": "safeoutputs", "index": { "$numberDouble": "17.0" }, "panelId": "safeoutputs-retention" } - ], - "questionPanels": [ - { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safepeople", - "navHeader": "Primary applicant", - "panelId": "primaryapplicant", - "panelHeader": "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", - "questionSets": [{ "questionSetId": "primaryapplicant", "index": { "$numberDouble": "1.0" } }] - }, - { - "navHeader": "Other individuals", - "panelHeader": "Please list the individuals who will have access to the data requested, or are responsible for helping complete this application form. \n\nThis section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.\n\nPlease use the file upload function if you're not able to add all individuals via the form.", - "panelId": "safepeople-otherindividuals", - "questionSets": [ - { "questionSetId": "safepeople-otherindividuals", "index": { "$numberDouble": "1.0" } }, - { "index": { "$numberDouble": "100.0" }, "questionSetId": "add-safepeople-otherindividuals" } - ], - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safepeople" + ] + }, + { + "key": "length_of_follow_up", + "dimension": "Length of follow up", + "category": "Coverage", + "definition": "Average timeframe in which a patient appears in a dataset (follow up period)", + "wizardStepTitle": "Length of follow up", + "wizardStepDescription": "What is the minimum required time frame for patients appearing in dataset, on average? (optional)", + "includeInWizard": true, + "wizardStepOrder": 3, + "wizardStepType": "radio", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83ef7fe40ac9c" + }, + "displayOrder": 1, + "definition": "Between 1-6 months", + "label": "Between 1-6 months", + "impliedValues": [ + "platinum", + "gold", + "silver", + "bronze" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83e7f7d40ac9d" + }, + "displayOrder": 2, + "definition": "Between 6-12 months", + "label": "Between 6-12 months", + "impliedValues": [ + "platinum", + "gold", + "silver" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83e41b040ac9e" + }, + "displayOrder": 3, + "definition": "Between 1-10 years", + "label": "Between 1-10 years", + "impliedValues": [ + "platinum", + "gold" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83ea2d940ac9f" + }, + "displayOrder": 4, + "definition": "More than 10 years", + "label": "More than 10 years", + "impliedValues": [ + "platinum" + ] }, { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safeproject", - "navHeader": "About this application", - "panelHeader": "", - "panelId": "safeproject-aboutthisapplication", - "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-aboutthisapplication" }] + "id": { + "$oid": "618a8b8ba5c83e56da40aca0" + }, + "displayOrder": 5, + "definition": "N/A", + "label": "N/A", + "impliedValues": [] + } + ] + }, + { + "key": "availability_of_additional_documentation_and_support", + "dimension": "Availability of additional documentation and support", + "category": "Data Documentation", + "definition": "Available dataset documentation in addition to the data dictionary", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e06f540aca1" + }, + "displayOrder": 1, + "definition": "As Gold, plus support personnel available to answer questions", + "impliedValues": [ + "platinum" + ] }, { - "questionSets": [{ "questionSetId": "safeproject-projectdetails", "index": { "$numberDouble": "1.0" } }], - "panelHeader": "", - "panelId": "safeproject-projectdetails", - "navHeader": "Project details", - "pageId": "safeproject", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83eb2e940aca2" + }, + "displayOrder": 2, + "definition": "As Silver, plus dataset publication was supported with a journal article explaining the dataset in detail, or dataset training materials", + "impliedValues": [ + "gold" + ] }, { - "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-funderinformation" }], - "panelHeader": "A funder is the organisation or body providing the financial resource to make the project possible, and may be different to the organisation detailed in the Safe people section. Please provide details of the main funder organisations supporting this project.\n\nPlease use the file upload function if you're not able to add all funders via the form.", - "panelId": "safeproject-funderinformation", - "navHeader": "Funder information", - "pageId": "safeproject", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83e7f2940aca3" + }, + "displayOrder": 3, + "definition": "Comprehensive ReadMe describing extracting and use of data, Dataset FAQS available, Visual data model provided", + "impliedValues": [ + "silver" + ] }, { - "navHeader": "Sponsor information", - "questionSets": [{ "questionSetId": "safeproject-sponsorinformation", "index": { "$numberDouble": "1.0" } }], - "panelId": "safeproject-sponsorinformation", - "panelHeader": "Please use the file upload function if you're not able to add all sponsors via the form.", - "pageId": "safeproject", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83e464340aca4" + }, + "displayOrder": 4, + "definition": "Past journal articles demonstrate that knowledge of the data exists", + "impliedValues": [ + "bronze" + ] + } + ] + }, + { + "key": "data_model", + "dimension": "Data Model", + "category": "Data Documentation", + "definition": "Availability of clear, documented data model", + "includeInWizard": true, + "wizardStepTitle": "Data model", + "wizardStepDescription": "What data model requirements do you have? (optional)", + "wizardStepOrder": 4, + "wizardStepType": "radio", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83eebd740aca5" + }, + "displayOrder": 1, + "definition": "Known and accepted data model but some key field un-coded or free text", + "label": "Known and accepted data model, with some key fields uncoded ", + "impliedValues": [ + "platinum", + "gold", + "silver", + "bronze" + ] }, { - "panelHeader": "", - "panelId": "safeproject-declarationofinterest", - "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safeproject-declarationofinterest" }], - "navHeader": "Declaration of interest", - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safeproject" + "id": { + "$oid": "618a8b8ba5c83eb37f40aca6" + }, + "displayOrder": 2, + "definition": "Key fields codified using a local standard", + "label": "Key fields coded using local standard", + "impliedValues": [ + "platinum", + "gold", + "silver" + ] }, { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safeproject", - "navHeader": "Intellectual property", - "panelId": "safeproject-intellectualproperty", - "panelHeader": "", - "questionSets": [{ "questionSetId": "safeproject-intellectualproperty", "index": { "$numberDouble": "1.0" } }] + "id": { + "$oid": "618a8b8ba5c83e636740aca7" + }, + "displayOrder": 3, + "definition": "Key fields codified using a national or international standard", + "label": "Key fields coded using national/international standard", + "impliedValues": [ + "platinum", + "gold" + ] }, { - "questionSets": [{ "questionSetId": "safedata-datafields", "index": { "$numberDouble": "1.0" } }], - "panelHeader": "", - "panelId": "safedata-datafields", - "navHeader": "Data fields", - "pageId": "safedata", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83effd740aca8" + }, + "displayOrder": 4, + "definition": "Data Model conforms to a national standard and key fields codified using a national/international standard", + "label": "Model conforms to national standard and key fields coded to national/internal standard", + "impliedValues": [ + "platinum" + ] }, { - "questionSets": [{ "questionSetId": "safedata-otherdatasetsintentiontolinkdata", "index": { "$numberDouble": "1.0" } }], - "panelHeader": "", - "panelId": "safedata-otherdatasetsintentiontolinkdata", - "navHeader": "Other datasets - Intention to link data", - "pageId": "safedata", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83e917e40aca9" + }, + "displayOrder": 5, + "definition": "N/A", + "label": "N/A", + "impliedValues": [] + } + ] + }, + { + "key": "data_dictionary", + "dimension": "Data Dictionary", + "category": "Data Documentation", + "definition": "Provided documented data dictionary and terminologies", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e619040acaa" + }, + "displayOrder": 1, + "definition": "Dictionary is based on international standards and includes mapping", + "impliedValues": [ + "platinum" + ] }, { - "navHeader": "Lawful basis", - "panelId": "safedata-lawfulbasis", - "panelHeader": "General Data Protection Regulation (GDPR) applies to ‘controllers’ and ‘processors’. \n\nA controller determines the purposes and means of processing personal data.\n\nA processor is responsible for processing personal data on behalf of a controller.\n \nGDPR applies to processing carried out by organisations operating within the EU. It also applies to organisations outside the EU that offer goods or services to individuals in the EU.\nGDPR does not apply to certain activities including processing covered by the Law Enforcement Directive, processing for national security purposes and processing carried out by individuals purely for personal/household activities. \n \nGDPR only applies to information which relates to an identifiable living individual. Information relating to a deceased person does not constitute personal data and therefore is not subject to the GDPR.", - "questionSets": [{ "questionSetId": "safedata-lawfulbasis", "index": { "$numberDouble": "1.0" } }], - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safedata" + "id": { + "$oid": "618a8b8ba5c83e283240acab" + }, + "displayOrder": 2, + "definition": "Dictionary relates to national definitions", + "impliedValues": [ + "gold" + ] }, { - "pageId": "safedata", - "questionPanelHeaderText": "TODO: We need a description for this panel", - "navHeader": "Confidentiality avenue", - "questionSets": [{ "questionSetId": "safedata-confidentialityavenue", "index": { "$numberDouble": "1.0" } }], - "panelHeader": "", - "panelId": "safedata-confidentialityavenue" + "id": { + "$oid": "618a8b8ba5c83e4c2840acac" + }, + "displayOrder": 3, + "definition": "Definitions compiled into local data dictionary which is available online", + "impliedValues": [ + "silver" + ] }, { - "pageId": "safedata", - "questionPanelHeaderText": "TODO: We need a description for this panel", - "questionSets": [{ "index": { "$numberDouble": "1.0" }, "questionSetId": "safedata-ethicalapproval" }], - "panelId": "safedata-ethicalapproval", - "panelHeader": "This section details the research and ethics approval which you have obtained or sought for your project, or otherwise provides evidence as to why such approval is not necessary. For instance, for data analysis done on data deemed anonymous, no ethical approval is explicitly needed for said analyses. \n\nWhere such approval is not in place, it is important that you demonstrate why this is the case and provide assurances if approval is pending. If you need advice on whether ethics approval is necessary, you should approach your local ethics services in the first instance. Information about UK research ethics committees and ethical opinions can be found on the Health Research Authority (HRA) website.", - "navHeader": "Ethical approval" + "id": { + "$oid": "618a8b8ba5c83eb4d840acad" + }, + "displayOrder": 4, + "definition": "Data definitions available", + "impliedValues": [ + "bronze" + ] + } + ] + }, + { + "key": "provenance", + "dimension": "Provenance", + "category": "Data Documentation", + "definition": "Clear description of source and history of the dataset, providing a \"transparent data pipeline\"", + "includeInWizard": true, + "wizardStepTitle": "Provenance", + "wizardStepDescription": "To what level of detail do you require the origin of the dataset to be documented? (optional)", + "wizardStepOrder": 5, + "wizardStepType": "radio", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e5baa40acae" + }, + "displayOrder": 1, + "definition": "Source of the dataset is documented", + "label": "Dataset source documented", + "impliedValues": [ + "platinum", + "gold", + "silver", + "bronze" + ] }, { - "questionSets": [{ "questionSetId": "safesettings-storageandprocessing", "index": { "$numberDouble": "1.0" } }], - "panelHeader": "This section details in what way the proposal aims to store and use data, and controls in place to minimise risks associated with this storage and use. If you have indicated that your proposal seeks to store and use data exclusively through a recognised trusted research environment, then you do not need to complete this section.\n In relation to personal data, means any operation or set of operations which is performed on personal data or on sets of personal data (whether or not by automated means, such as collection, recording, organisation, structuring, storage, alteration, retrieval, consultation, use, disclosure, dissemination, restriction, erasure or destruction).\n \n All Locations where processing will be undertaken, for the avoidance of doubt storage is considered processing. For each separate organisation processing data which is not fully anonymous a separate partner organisation form must also be completed.\n \n Processing, in relation to information or data means obtaining, recording or holding the information or data or carrying out any operation or set of operations on the information or data, including—\n a) organisation, adaptation or alteration of the information or data,\n b) retrieval, consultation or use of the information or data,\n c) disclosure of the information or data by transmission,\n dissemination or otherwise making available, or\n d) alignment, combination, blocking, erasure or destruction of the information or data.\n\nPlease note that where an applicant has explicit consent from participants (such as in cohort studies) to hold linked health records, and therefore request extraction of data held by SAIL back to their own research setting, rather than be accessed solely in the remote desktop environment SAIL providers, a user may apply in these circumstances to SAIL for such extraction, with justification provided through the means of their consent documentation and ethical approval to hold such data outside of our secure setting.\n\nPlease use the file upload function if you're not able to add all organisations via the form.", - "panelId": "safesettings-storageandprocessing", - "navHeader": "Storage and processing", - "pageId": "safesettings", - "questionPanelHeaderText": "TODO: We need a description for this panel" + "id": { + "$oid": "618a8b8ba5c83e12d940acaf" + }, + "displayOrder": 2, + "definition": "Source of the dataset and any transformations, rules and exclusions documented", + "label": "Dataset source, any transformations, rule and exclusions documented", + "impliedValues": [ + "platinum", + "gold", + "silver" + ] }, { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safesettings", - "panelHeader": "", - "panelId": "safesettings-dataflow", - "questionSets": [{ "questionSetId": "safesettings-dataflow", "index": { "$numberDouble": "1.0" } }], - "navHeader": "Dataflow" - }, + "id": { + "$oid": "618a8b8ba5c83e4d7340acb0" + }, + "displayOrder": 3, + "definition": "All original data items listed, all transformations, rules and exclusion listed and impact of these", + "label": "All original data items, transformations, rules, exclusions and impact listed", + "impliedValues": [ + "platinum", + "gold" + ] + }, { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safeoutputs", - "panelId": "safeoutputs-outputsdisseminationplans", - "panelHeader": "", - "questionSets": [{ "questionSetId": "safeoutputs-outputsdisseminationplans", "index": { "$numberDouble": "1.0" } }], - "navHeader": "Outputs dissemination plans" - }, + "id": { + "$oid": "618a8b8ba5c83e550740acb1" + }, + "displayOrder": 4, + "definition": "Ability to view earlier versions, including versions before any transformations have been applied data (in line with deidentification and IG approval) and review the impact of each stage of data cleaning", + "label": "Earlier and 'raw' versions and the impact of each stage of data cleaning", + "impliedValues": [ + "platinum" + ] + }, { - "questionPanelHeaderText": "TODO: We need a description for this panel", - "pageId": "safeoutputs", - "panelHeader": "", - "panelId": "safeoutputs-retention", - "questionSets": [{ "questionSetId": "safeoutputs-retention", "index": { "$numberDouble": "1.0" } }], - "navHeader": "Retention" + "id": { + "$oid": "618a8b8ba5c83e32c140acb2" + }, + "displayOrder": 5, + "definition": "N/A", + "label": "N/A", + "impliedValues": [] } - ], - "questionSets": [ - { - "questionSetId": "primaryapplicant", - "questionSetHeader": "Primary applicant", - "questions": [ - { - "guidance": "Please insert your full name.", - "validations": [{ "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], - "questionId": "safepeopleprimaryapplicantfullname", - "input": { "type": "textInput", "required": true }, - "question": "Full name", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Job title", - "questionId": "safepeopleprimaryapplicantjobtitle", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], - "input": { "required": true, "type": "textInput" }, - "lockedQuestion": 1, - "defaultQuestion": 1 - }, - { - "questionId": "safepeopleprimaryapplicanttelephone", - "input": { "type": "textInput" }, - "question": "Telephone", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "ORCID", - "input": { "type": "textInput" }, - "questionId": "safepeopleprimaryapplicantorcid", - "guidance": "ORCID provides a persistent digital identifier (an ORCID iD) that you own and control, and that distinguishes you from every other researcher. You can create an ORCID profile at https://orcid.org/. If you have an ORCID iD please include it here. ", - "lockedQuestion": 0, - "defaultQuestion": 1 - }, - { - "question": "Email", - "validations": [ - { "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }, - { "type": "isEmail" } - ], - "input": { "required": true, "type": "textInput" }, - "questionId": "safepeopleprimaryapplicantemail", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { - "options": [ - { "value": "Yes", "text": "Yes" }, - { "value": "No", "text": "No" } - ], - "label": "Will you access the data requested?", - "type": "radioOptionsInput" - }, - "questionId": "safepeopleprimaryapplicantaccessdata", - "guidance": "Please confirm whether the applicant will be accessing the data that is being requested.", - "question": "Will you access the data requested?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Are you an accredited researcher under the Digital Economy Act 2017?", - "input": { - "label": "Are you an accredited researcher under the Digital Economy Act 2017?", - "type": "radioOptionsInput", - "options": [ - { - "text": "Yes", - "value": "Yes", - "conditionalQuestions": [ - { - "question": "If yes, please provide your accredited researcher number.", - "input": { "type": "textareaInput" }, - "questionId": "safepeopleprimaryapplicantaccreditedresearchernumber" - } - ] - }, - { - "value": "No", - "text": "No", - "conditionalQuestions": [ - { - "questionId": "safepeopleprimaryapplicantaccreditedresearcherintention", - "input": { "type": "textareaInput" }, - "question": "If no, please specify if you are planning to become an accredited researcher." - } - ] - } - ] - }, - "questionId": "safepeopleprimaryapplicantaccreditedresearcher", - "guidance": "Depending on the type of data you are requesting, you might be required to become an accredited researcher. Most access to data in the Secure Research Service (SRS) will be by researchers accredited under the Digital Economy Act 2017 (DEA). \n\nThe UK Statistics Authority has published further information on the criteria to be met in a Research Code of Practice and Accreditation criteria. Researchers can apply for accreditation through the Research Accreditation Service (RAS).\n\nFull accredited researcher status is valid for five years. Provisional accredited researcher status is valid for one year.\n\nMore information here: https://www.gov.uk/government/publications/digital-economy-act-2017-part-5-codes-of-practice/research-code-of-practice-and-accreditation-criteria#section-b-accreditation-of-researchers-and-peer-reviewers", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Have you undertaken professional training or education on the topic of Information Governance?", - "questionId": "safepeopleprimaryapplicanttraininginformationgovernance", - "input": { - "options": [ - { - "conditionalQuestions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safepeopleprimaryapplicanttraininginformationgovernancerecent", - "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", - "question": "Please provide full details regarding the most recent training" - } - ], - "text": "Yes", - "value": "Yes" - }, - { - "text": "No", - "value": "No", - "conditionalQuestions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safepeopleprimaryapplicanttraininginformationgovernanceintention", - "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", - "question": "Please provide any details of plans to attend training, if applicable" - } - ] - } - ], - "label": "Have you undertaken professional training or education on the topic of Information Governance?", - "type": "radioOptionsInput" - }, - "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safepeopleprimaryapplicantorganisationname", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], - "input": { "type": "textInput", "required": true }, - "guidance": "Please give the full name of the organisation on whose behalf you are making the application or within which you work in your professional capacity as an applicant. This should include a parent organisation, and sub-division or department if appropriate (for example University of Edinburgh, Department of Informatics).", - "question": "Your organisation name", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], - "input": { - "type": "radioOptionsInput", - "required": true, - "label": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", - "options": [ - { - "text": "Yes", - "value": "Yes", - "conditionalQuestions": [ - { - "question": "If yes, please provide the current status", - "input": { "type": "textInput" }, - "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitstatus" - }, - { - "question": "If yes, please provide the date published", - "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitdatepublished", - "input": { "type": "datePickerCustom" }, - "validations": [{ "type": "isCustomDate" }] - } - ] - }, - { "value": "No", "text": "No" } - ] - }, - "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkit", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Will your organisation act as data controller?", - "input": { - "type": "radioOptionsInput", - "label": "Will your organisation act as data controller?", - "options": [ - { - "conditionalQuestions": [ - { - "question": "ICO registered number", - "questionId": "safepeopleprimaryapplicantorganisationicoregisterednumber", - "input": { "type": "textInput" } - }, - { - "question": "Registered address (line 1)", - "guidance": "Please include the organisation's business address", - "questionId": "safepeopleprimaryapplicantorganisationaddressline1", - "input": { "type": "textInput" } - }, - { - "question": "Registered address (line 2)", - "input": { "type": "textInput" }, - "questionId": "safepeopleprimaryapplicantorganisationaddressline2", - "guidance": "Please include the organisation's business address" - }, - { - "question": "City", - "guidance": "Please specify the city where the organisation is located", - "questionId": "safepeopleprimaryapplicantorganisationcity", - "input": { "type": "textInput" } - }, - { - "question": "Postcode", - "guidance": "Please include the organisation's business address postcode.", - "input": { "type": "textInput" }, - "questionId": "safepeopleprimaryapplicantorganisationpostcode" - }, - { - "question": "Country", - "questionId": "safepeopleprimaryapplicantorganisationcountry", - "input": { "type": "textInput" }, - "guidance": "Please specify the country where the organisation is located" - }, - { - "question": "Organisation type", - "label": "Organisation type", - "guidance": "Please select type of organisation, unique purpose or role of the organisation", - "questionId": "safepeopleprimaryapplicantorganisationtype", - "input": { - "options": [ - { "text": "Academic institution", "value": "Academic institution" }, - { "value": "National body", "text": "National body" }, - { "value": "Healthcare provider", "text": "Healthcare provider" }, - { "text": "Healthcare comissioner", "value": "Healthcare comissioner" }, - { "text": "Commercial body", "value": "Commercial body" }, - { "text": "Local Authority", "value": "Local Authority" }, - { "value": "Other", "text": "Other" } - ], - "type": "checkboxOptionsInput" - } - }, - { - "question": "Please provide details", - "input": { "type": "textareaInput" }, - "questionId": "safepeopleprimaryapplicantorganisationtypeotherdetails" - } - ], - "text": "Yes", - "value": "Yes" - }, - { "value": "No", "text": "No" }, - { "value": "Unsure", "text": "Unsure" } - ] - }, - "questionId": "safepeopleprimaryapplicantorganisationdatacontroller", - "guidance": "Please specify if your organisation will act as a data controller. If your organisation is not the sole data controller, please provide details of other data controllers. ", - "lockedQuestion": 1, - "defaultQuestion": 0 - } - ] + ] + }, + { + "includeInWizard": true, + "wizardStepTitle": "Search terms", + "wizardStepDescription": "Please type in any relevant search terms to refine your search (optional)", + "wizardStepOrder": 6, + "wizardStepType": "search", + "entries": [] + }, + { + "key": "linkages", + "dimension": "Linkages", + "category": "Value & Interest", + "definition": "Ability to link with other datasets", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83eae8640acb3" + }, + "displayOrder": 1, + "definition": "Existing linkage with reusable or downstream approvals", + "impliedValues": [ + "platinum" + ] }, { - "questionSetHeader": "Other individuals", - "questionSetId": "safepeople-otherindividuals", - "questions": [ - { - "question": "Full name", - "input": { "type": "textInput" }, - "questionId": "safepeopleotherindividualsfullname", - "guidance": "Full name is the individual's first and last name", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Job title", - "input": { "type": "textInput" }, - "questionId": "safepeopleotherindividualsjobtitle", - "guidance": "Job Title is the name of the position the individual holds within their organisation.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Organisation", - "questionId": "safepeopleotherindividualsorganisation", - "input": { "type": "textInput" }, - "guidance": "Please include the individual's organisation.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { - "type": "checkboxOptionsInput", - "label": "Role", - "options": [ - { "value": "Principal investigator", "text": "Principal investigator" }, - { "text": "Collaborator", "value": "Collaborator" }, - { "value": "Team member", "text": "Team member" }, - { - "value": "Other", - "text": "Other", - "conditionalQuestions": [ - { - "questionId": "safepeopleotherindividualsroleotherdetails", - "input": { "type": "textareaInput" }, - "question": "If other, please specify" - } - ] - } - ] - }, - "questionId": "safepeopleotherindividualsrole", - "guidance": "A role is a function that the applicant plays. It might include role types and accreditation for those that are accessing the secure data and those that are not but would see cleared outputs from the project. \r\n (i.e. project lead, deputy lead, accrediter, researcher, peer reviewer)", - "question": "Role", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Will this person access the data requested?", - "questionId": "safepeopleotherindividualsaccessdata", - "input": { - "options": [ - { "value": "Yes", "text": "Yes" }, - { "text": "No", "value": "No" } - ], - "label": "Will this person access the data requested?", - "type": "radioOptionsInput" - }, - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safepeopleotherindividualsaccreditedresearcher", + "id": { + "$oid": "618a8b8ba5c83ee0f540acb4" + }, + "displayOrder": 2, + "definition": "List of restrictions on the type of linkages detailed. List of previously successful dataset linkages performed, with navigable links to linked datasets via at DOI/URL", + "impliedValues": [ + "gold" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83e56a940acb5" + }, + "displayOrder": 3, + "definition": "Available linkages outlined and/or List of datasets previously successfully linked provided", + "impliedValues": [ + "silver" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83ee6f540acb6" + }, + "displayOrder": 4, + "definition": "Identifiers to demonstrate ability to link to other datasets", + "impliedValues": [ + "bronze" + ] + } + ] + }, + { + "key": "data_enrichments", + "dimension": "Data Enrichments", + "category": "Value & Interest", + "definition": "Data sources enriched with annotations, image labels, phenomes, derivations, NLP derived data labels", + "entries": [ + { + "id": { + "$oid": "618a8b8ba5c83e9b8940acb7" + }, + "displayOrder": 1, + "definition": "The data include additional derived fields, or enriched data", + "impliedValues": [ + "platinum" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83e139e40acb8" + }, + "displayOrder": 2, + "definition": "The data include additional derived fields, or enriched data used by other available data sources", + "impliedValues": [ + "gold" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83e4a9d40acb9" + }, + "displayOrder": 3, + "definition": "The derived fields or enriched data were generated from, or used by, a peer reviewed algorithm", + "impliedValues": [ + "silver" + ] + }, + { + "id": { + "$oid": "618a8b8ba5c83eaa7140acba" + }, + "displayOrder": 4, + "definition": "The data includes derived fields or enriched data from a national report", + "impliedValues": [ + "bronze" + ] + } + ] + } + ] + }, + "masterSchema": { + "pages": [ + { + "description": "Who is going to be accessing the data?\n\nSafe People should have the right motivations for accessing research data and understand the legal and ethical considerations when using data that may be sensitive or confidential. Safe People should also have sufficient skills, knowledge and experience to work with the data effectively. Researchers may need to undergo specific training or accreditation before accessing certain data or research environments and demonstrate that they are part of a bona fide research organisation.\n\nThe purpose of this section is to ensure that:\n- details of people who will be accessing the data and the people who are responsible for completing the application are identified\n- any individual or organisation that intends to access the data requested is identified\n- all identified individuals have the necessary accreditation and/or expertise to work with the data effectively.", + "title": "Safe people", + "pageId": "safepeople", + "active": true + }, + { + "active": false, + "pageId": "safeproject", + "title": "Safe project", + "description": "What is the purpose of accessing the data?\n\nSafe projects are those that have a valid research purpose with a defined public benefit. \nFor access to data to be granted the researchers need to demonstrate that their proposal is an appropriate and ethical use of the data, and that it is intended to deliver clear public benefits. The purpose of this section is to ensure that:\n- the project rationale is explained in lay terms\n- the research purpose has a defined public benefit. This can be new knowledge, new treatments, improved pathways of care, new techniques of training staff. \n- how the data requested will be used to achieve the project objectives is articulated." + }, + { + "active": false, + "description": "Safe data ensure that researchers have a clear legal basis for accessing the data and do not inadvertently learn something about the data subjects during the course of their analysis, minimising the risks of re-identification.\nThe minimisation of this risk could be achieved by removing direct identifiers, aggregating values, banding variables, or other statistical techniques that may make re-identification more difficult. Sensitive or confidential data could not be considered to be completely safe because of the residual risk to a data subject’s confidentiality. Hence other limitations on access will need to be applied.\n\nThe purpose of this section is to ensure that: \n- there is a clear legal basis for accessing the requested data\n- the data requested is proportionate to the requirement of the project \n- all data requested is necessary in order to achieve the public benefit declared \n- data subjects cannot be identified by your team by cross-referencing datasets from anywhere else.", + "title": "Safe data", + "pageId": "safedata" + }, + { + "pageId": "safesettings", + "title": "Safe settings", + "description": "Safe settings are analytics environments where researchers can access and analyse the requested datasets in a safe and ethical way. Safe settings encompass the physical environment and procedural arrangements such as the supervision and auditing regimes. For safe settings, the likelihood of both deliberate and accidental disclosure needs to be explicitly considered.\n\nThe purpose of this section is to ensure that:\n\n- researchers access requested data in a secure and controlled setting such as a Trusted Research Environment (TRE) that limits the unauthorised use of the data\n- practical controls and appropriate restrictions are in place if researchers access data though non-TRE environment. There may be requirements that data is held on restricted access servers, encrypted and only decrypted at the point of use.", + "active": false + }, + { + "description": "Safe outputs ensure that all research outputs cannot be used to identify data subjects. They typically include ‘descriptive statistics’ that have been sufficiently aggregated such that identification is near enough impossible, and modelled outputs which are inherently non-confidential. The purpose of this section is to ensure that:\n\n- controls are in place to minimise risks associated with planned outputs and publications \n- the researchers aim to openly publish their results to enable use, scrutiny and further research.", + "pageId": "safeoutputs", + "title": "Safe outputs", + "active": false + }, + { + "active": false, + "description": "Any additional information or files requested by the Data Custodian of the datasets you are requesting to access can be uploaded in this section.", + "title": "Additional information & files", + "pageId": "additionalinformationfiles" + } + ], + "formPanels": [ + { + "panelId": "safepeople-primaryapplicant", + "index": 1, + "pageId": "safepeople" + }, + { + "panelId": "safepeople-otherindividuals", + "pageId": "safepeople", + "index": 2 + }, + { + "panelId": "safeproject-aboutthisapplication", + "pageId": "safeproject", + "index": 3 + }, + { + "panelId": "safeproject-projectdetails", + "pageId": "safeproject", + "index": 4 + }, + { + "index": 5, + "pageId": "safeproject", + "panelId": "safeproject-funderinformation" + }, + { + "pageId": "safeproject", + "index": 6, + "panelId": "safeproject-sponsorinformation" + }, + { + "index": 7, + "pageId": "safeproject", + "panelId": "safeproject-declarationofinterest" + }, + { + "panelId": "safeproject-intellectualproperty", + "pageId": "safeproject", + "index": 8 + }, + { + "panelId": "safedata-datafields", + "index": 9, + "pageId": "safedata" + }, + { + "index": 10, + "pageId": "safedata", + "panelId": "safedata-analysis" + }, + { + "pageId": "safedata", + "index": 11, + "panelId": "safedata-samplesrequired" + }, + { + "index": 12, + "pageId": "safedata", + "panelId": "safedata-otherdatasetsintentiontolinkdata" + }, + { + "panelId": "safedata-statisticalanalysis", + "index": 13, + "pageId": "safedata" + }, + { + "pageId": "safedata", + "index": 14, + "panelId": "safedata-lawfulbasis" + }, + { + "pageId": "safedata", + "index": 15, + "panelId": "safedata-confidentialityavenue" + }, + { + "panelId": "safedata-ethicalapproval", + "pageId": "safedata", + "index": 16 + }, + { + "index": 17, + "pageId": "safesettings", + "panelId": "safesettings-storageandprocessing" + }, + { + "panelId": "safesettings-dataflow", + "pageId": "safesettings", + "index": 18 + }, + { + "index": 19, + "pageId": "safeoutputs", + "panelId": "safeoutputs-outputsdisseminationplans" + }, + { + "pageId": "safeoutputs", + "index": 20, + "panelId": "safeoutputs-retention" + }, + { + "panelId": "safeoutputs-archiving", + "index": 21, + "pageId": "safeoutputs" + }, + { + "pageId": "additionalinformationfiles", + "index": 22, + "panelId": "additionalinformationfiles-invoicecontact" + }, + { + "pageId": "additionalinformationfiles", + "index": 23, + "panelId": "additionalinformationfiles-datasharingagreementsignatory" + } + ], + "questionPanels": [ + { + "questionSets": [ + { + "index": 1, + "questionSetId": "safepeople-primaryapplicant" + } + ], + "pageId": "safepeople", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "This section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.", + "navHeader": "Primary applicant", + "panelId": "safepeople-primaryapplicant" + }, + { + "pageId": "safepeople", + "questionSets": [ + { + "questionSetId": "safepeople-otherindividuals", + "index": 1 + }, + { + "index": 100, + "questionSetId": "add-safepeople-otherindividuals" + } + ], + "navHeader": "Other individuals", + "panelId": "safepeople-otherindividuals", + "panelHeader": "This section should include key contact details for the person who is leading the project; key contact details for the person(s) who (are) leading the project from other organisations. Only one contact from each organisation is needed. \n\nThe 'Primary applicant' is the person filling out the application form and principal contact for the application. This is usually the person with operational responsibility for the proposal. Each application must have details for at least one person.", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "pageId": "safeproject", + "questionSets": [ + { + "index": 1, + "questionSetId": "safeproject-aboutthisapplication" + } + ], + "navHeader": "About this application", + "panelId": "safeproject-aboutthisapplication", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "" + }, + { + "panelHeader": "", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "navHeader": "Project details", + "panelId": "safeproject-projectdetails", + "questionSets": [ + { + "index": 1, + "questionSetId": "safeproject-projectdetails" + } + ], + "pageId": "safeproject" + }, + { + "questionSets": [ + { + "index": 1, + "questionSetId": "safeproject-funderinformation" + } + ], + "pageId": "safeproject", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "A funder is the organisation or body providing the financial resource to make the project possible, and may be different to the organisation detailed in the Safe people section. Please provide details of the main funder organisations supporting this project.\n\nPlease use the file upload function if you're not able to add all funders via the form.\n", + "panelId": "safeproject-funderinformation", + "navHeader": "Funder information" + }, + { + "pageId": "safeproject", + "questionSets": [ + { + "index": 1, + "questionSetId": "safeproject-sponsorinformation" + } + ], + "panelId": "safeproject-sponsorinformation", + "navHeader": "Sponsor information", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "The sponsor is usually, but does not have to be, the applicant’s substantive employer. The sponsor takes primary responsibility for ensuring that the design of the project meets appropriate standards and that arrangements are in place to ensure appropriate conduct and reporting.\n\nPlease use the file upload function if you're not able to add all sponsors via the form.\n" + }, + { + "pageId": "safeproject", + "questionSets": [ + { + "questionSetId": "safeproject-declarationofinterest", + "index": 1 + } + ], + "panelId": "safeproject-declarationofinterest", + "navHeader": "Declaration of interest", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "All interests that might unduly influence an individual’s judgement and objectivity in the use of the data being requested are of relevance, particularly if it involves payment or financial inducement. \n\nThese might include any involvement of commercial organisations at arm’s-length to the project, or likely impact on commercial organisations, individually or collectively, that might result from the outcomes or methodology of the project.\n\nAll individuals named in this application who have an interest this application must declare their interest.\n" + }, + { + "navHeader": "Intellectual property", + "panelId": "safeproject-intellectualproperty", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "All interests that might unduly influence an individual’s judgement and objectivity in the use of the data being requested are of relevance, particularly if it involves payment or financial inducement. \n\nThese might include any involvement of commercial organisations at arm’s-length to the project, or likely impact on commercial organisations, individually or collectively, that might result from the outcomes or methodology of the project.\n\nAll individuals named in this application who have an interest this application must declare their interest.\n", + "pageId": "safeproject", + "questionSets": [ + { + "questionSetId": "safeproject-intellectualproperty", + "index": 1 + } + ] + }, + { + "pageId": "safedata", + "questionSets": [ + { + "index": 1, + "questionSetId": "safedata-datafields" + } + ], + "panelId": "safedata-datafields", + "navHeader": "Data fields", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "These are the Information assets which your project seeks to access and use. You should consider this definition to be wide in scope and include any source of information which you propose to access and use. The data may be highly structured or less structured in nature, already existing or to be newly collected or gathered. \n\nExamples may include national datasets, local data sets, national or local extracts from systems, national or local registries or networks, patient records, or new information to be gathered from patients, families or other cohorts. It could also include metadata which may need to be accessed when physical samples are being requested.\n\n“New data” should only include data that is being specifically gathered for the first time for the purposes of this project. i.e., data already held in case notes and transferred to a form is not “new” data, but a survey filled out by clinicians in order to gather information not recorded anywhere else is “new”." + }, + { + "questionSets": [ + { + "index": 1, + "questionSetId": "safedata-analysis" + } + ], + "pageId": "safedata", + "panelHeader": "These are the Information assets which your project seeks to access and use. You should consider this definition to be wide in scope and include any source of information which you propose to access and use. The data may be highly structured or less structured in nature, already existing or to be newly collected or gathered. \n\nExamples may include national datasets, local data sets, national or local extracts from systems, national or local registries or networks, patient records, or new information to be gathered from patients, families or other cohorts. It could also include metadata which may need to be accessed when physical samples are being requested.\n\n“New data” should only include data that is being specifically gathered for the first time for the purposes of this project. i.e., data already held in case notes and transferred to a form is not “new” data, but a survey filled out by clinicians in order to gather information not recorded anywhere else is “new”.", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelId": "safedata-analysis", + "navHeader": "Analysis" + }, + { + "pageId": "safedata", + "questionSets": [ + { + "index": 1, + "questionSetId": "safedata-samplesrequired" + } + ], + "panelId": "safedata-samplesrequired", + "navHeader": "Samples required", + "panelHeader": "These are the Information assets which your project seeks to access and use. You should consider this definition to be wide in scope and include any source of information which you propose to access and use. The data may be highly structured or less structured in nature, already existing or to be newly collected or gathered. \n\nExamples may include national datasets, local data sets, national or local extracts from systems, national or local registries or networks, patient records, or new information to be gathered from patients, families or other cohorts. It could also include metadata which may need to be accessed when physical samples are being requested.\n\n“New data” should only include data that is being specifically gathered for the first time for the purposes of this project. i.e., data already held in case notes and transferred to a form is not “new” data, but a survey filled out by clinicians in order to gather information not recorded anywhere else is “new”.", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "pageId": "safedata", + "questionSets": [ + { + "questionSetId": "safedata-otherdatasetsintentiontolinkdata", + "index": 1 + } + ], + "panelId": "safedata-otherdatasetsintentiontolinkdata", + "navHeader": "Other datasets - Intention to link data", + "panelHeader": "This section should include information on the planned use of datasets not already included in this application. The following information is required:\n\nA descriptive name so that it is clear what the dataset is. \n\nSufficient information to explain the content of the dataset. \n\nWhether the proposal requires linkage of data, the use of matched controls, or the extraction of anonymised data.\n\nPlease indicate which organisation or body is undertaking these processes and which variables from the data sources requested will be used to achieve the proposed linkage. This should cover every dataset and variable you will require.\n", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "", + "panelId": "safedata-statisticalanalysis", + "navHeader": "Statistical analysis", + "questionSets": [ + { + "questionSetId": "safedata-statisticalanalysis", + "index": 1 + } + ], + "pageId": "safedata" + }, + { + "navHeader": "Lawful basis", + "panelId": "safedata-lawfulbasis", + "panelHeader": "General Data Protection Regulation (GDPR) applies to ‘controllers’ and ‘processors’. \n\nA controller determines the purposes and means of processing personal data.\n\nA processor is responsible for processing personal data on behalf of a controller.\n \nGDPR applies to processing carried out by organisations operating within the EU. It also applies to organisations outside the EU that offer goods or services to individuals in the EU.\nGDPR does not apply to certain activities including processing covered by the Law Enforcement Directive, processing for national security purposes and processing carried out by individuals purely for personal/household activities. \n \nGDPR only applies to information which relates to an identifiable living individual. Information relating to a deceased person does not constitute personal data and therefore is not subject to the GDPR.\n", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safedata", + "questionSets": [ + { + "index": 1, + "questionSetId": "safedata-lawfulbasis" + } + ] + }, + { + "navHeader": "Confidentiality avenue", + "panelId": "safedata-confidentialityavenue", + "panelHeader": "If confidential information is being disclosed, the organisations holding this data (both the organisation disclosing the information and the recipient organisation) must also have a lawful basis to hold and use this information, and if applicable, have a condition to hold and use special categories of confidential information, and be fair and transparent about how they hold and use this data. \n\nIn England and Wales, if you are using section 251 of the NHS Act 2006 (s251) as a legal basis for identifiable data, you will need to ensure that you have the latest approval letter and application. \n\nFor Scotland this application will be reviewed by the Public Benefit and Privacy Panel.\n\nIn Northern Ireland it will be considered by the Privacy Advisory Committee. If you are using patient consent as the legal basis, you will need to provide all relevant consent forms and information leaflets.\n", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "pageId": "safedata", + "questionSets": [ + { + "questionSetId": "safedata-confidentialityavenue", + "index": 1 + } + ] + }, + { + "panelId": "safedata-ethicalapproval", + "navHeader": "Ethical approval", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "This section details the research and ethics approval which you have obtained or sought for your project, or otherwise provides evidence as to why such approval is not necessary. \nWhere such approval is not in place, it is important that you demonstrate why this is the case and provide assurances if approval is pending. If you need advice on whether ethics approval is necessary, you should approach your local ethics services in the first instance. Information about UK research ethics committees and ethical opinions can be found on the Health Research Authority (HRA) website.\n", + "pageId": "safedata", + "questionSets": [ + { + "questionSetId": "safedata-ethicalapproval", + "index": 1 + } + ] + }, + { + "panelId": "safesettings-storageandprocessing", + "navHeader": "Storage and processing", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "This section details in what way the proposal aims to store and use data, and controls in place to minimise risks associated with this storage and use. If you have indicated that your proposal seeks to store and use data exclusively through a recognised trusted research environment, then you do not need to complete this section.\n \nIn relation to personal data, means any operation or set of operations which is performed on personal data or on sets of personal data (whether or not by automated means, such as collection, recording, organisation, structuring, storage, alteration, retrieval, consultation, use, disclosure, dissemination, restriction, erasure or destruction).\n \nAll Locations where processing will be undertaken, for the avoidance of doubt storage is considered processing. For each separate organisation processing data which is not fully anonymous a separate partner organisation form must also be completed.\n \n Processing, in relation to information or data means obtaining, recording or holding the information or data or carrying out any operation or set of operations on the information or data, including—\n a) organisation, adaptation or alteration of the information or data,\n b) retrieval, consultation or use of the information or data,\n c) disclosure of the information or data by transmission,\n dissemination or otherwise making available, or\n d) alignment, combination, blocking, erasure or destruction of the information or data.\n\nPlease use the file upload function if you're not able to add all organisations via the form. Documents can be uploaded in the Additional Files section of this form.\n", + "pageId": "safesettings", + "questionSets": [ + { + "questionSetId": "safesettings-storageandprocessing", + "index": 1 + } + ] + }, + { + "panelHeader": "", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelId": "safesettings-dataflow", + "navHeader": "Dataflow", + "questionSets": [ + { + "index": 1, + "questionSetId": "safesettings-dataflow" + } + ], + "pageId": "safesettings" + }, + { + "pageId": "safeoutputs", + "questionSets": [ + { + "index": 1, + "questionSetId": "safeoutputs-outputsdisseminationplans" + } + ], + "panelId": "safeoutputs-outputsdisseminationplans", + "navHeader": "Outputs dissemination plans", + "panelHeader": "Please include any plans for dissemination and publication of the data and results arising from your proposal. Please also specify any controls in place to minimise risks associated with publication. Dissemination can take place in a variety of ways and through many mechanisms, including through electronic media, print media or word of mouth.", + "questionPanelHeaderText": "TODO: We need a description for this panel" + }, + { + "questionSets": [ + { + "index": 1, + "questionSetId": "safeoutputs-retention" + } + ], + "pageId": "safeoutputs", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "This section details how the project will treat data being processed after it has been used for the purpose of the proposal outlined, including governance in place to determine how long it will be retained, and controls to manage its subsequent disposal if required. Please reference any relevant policies and procedures which are in place to govern retention and disposal of data as outlined in the proposal.", + "panelId": "safeoutputs-retention", + "navHeader": "Retention" + }, + { + "questionSets": [ + { + "questionSetId": "safeoutputs-archiving", + "index": 1 + } + ], + "pageId": "safeoutputs", + "panelHeader": "This section details how the project will treat data being processed after it has been used for the purpose of the proposal outlined, including governance in place to determine how long it will be retained, and controls to manage its subsequent disposal if required. Please reference any relevant policies and procedures which are in place to govern retention and disposal of data as outlined in the proposal.", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "navHeader": "Archiving", + "panelId": "safeoutputs-archiving" + }, + { + "panelHeader": "Some custodians will only process Data Access Requests which include contact details for the invoice contact and the Data Sharing Agreement contact. If these questions are visible, it means that you have selected datasets from custodians who require this information. These contacts will only be approached if your application is approved.", + "questionPanelHeaderText": "TODO: We need a description for this panel", + "navHeader": "Invoice contact", + "panelId": "additionalinformationfiles-invoicecontact", + "questionSets": [ + { + "index": 1, + "questionSetId": "additionalinformationfiles-invoicecontact" + } + ], + "pageId": "additionalinformationfiles" + }, + { + "questionPanelHeaderText": "TODO: We need a description for this panel", + "panelHeader": "Some custodians will only process Data Access Requests which include contact details for the invoice contact and the Data Sharing Agreement contact. If these questions are visible, it means that you have selected datasets from custodians who require this information. These contacts will only be approached if your application is approved.", + "navHeader": "Data Sharing Agreement signatory", + "panelId": "additionalinformationfiles-datasharingagreementsignatory", + "questionSets": [ + { + "questionSetId": "additionalinformationfiles-datasharingagreementsignatory", + "index": 1 + } + ], + "pageId": "additionalinformationfiles" + } + ], + "questionSets": [ + { + "questionSetId": "safepeople-primaryapplicant", + "questionSetHeader": "Primary applicant", + "questions": [ + { + "questionId": "safepeopleprimaryapplicantfullname", + "defaultQuestion": 1, + "lockedQuestion": 1, + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + } + ], + "guidance": "Please insert your full name.", + "question": "Full name" + }, + { + "question": "Job title", + "guidance": "Job title is the name of the position the applicant holds within their organisation.", + "lockedQuestion": 1, + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicantjobtitle" + }, + { + "input": { + "type": "textInput" + }, + "lockedQuestion": 0, + "guidance": "Please include a contact telephone number that the applicant can be contacted on.", + "question": "Telephone", + "questionId": "safepeopleprimaryapplicanttelephone", + "defaultQuestion": 1 + }, + { + "questionId": "safepeopleprimaryapplicantorcid", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "type": "textInput" + }, + "guidance": "ORCID provides a persistent digital identifier (an ORCID iD) that you own and control, and that distinguishes you from every other researcher. You can create an ORCID profile at https://orcid.org/. If you have an ORCID iD please include it here. ", + "question": "ORCID" + }, + { + "lockedQuestion": 1, + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + }, + { + "type": "isEmail" + } + ], + "guidance": "Please include an email address that the applicant can receive communications through.", + "question": "Email", + "questionId": "safepeopleprimaryapplicantemail", + "defaultQuestion": 1 + }, + { + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicantaccessdata", + "question": "Will you access the data requested?", + "guidance": "Please confirm whether the applicant will be accessing the data that is being requested.", + "lockedQuestion": 1, + "input": { + "options": [ + { + "value": "Yes", + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } + ], + "label": "Will you access the data requested?", + "type": "radioOptionsInput" + } + }, + { + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicantaccreditedresearcher", + "guidance": "Depending on the type of data you are requesting, you might be required to become an accredited researcher. Most access to data in the Secure Research Service (SRS) will be by researchers accredited under the Digital Economy Act 2017 (DEA). \n\nThe UK Statistics Authority has published further information on the criteria to be met in a Research Code of Practice and Accreditation criteria. Researchers can apply for accreditation through the Research Accreditation Service (RAS).\n\nFull accredited researcher status is valid for five years. Provisional accredited researcher status is valid for one year.\n\nMore information here: https://www.gov.uk/government/publications/digital-economy-act-2017-part-5-codes-of-practice/research-code-of-practice-and-accreditation-criteria#section-b-accreditation-of-researchers-and-peer-reviewers\n\nPlease note if you only need to review cleared outputs from the research, you do NOT need to be accredited for this purpose.", + "question": "Are you an accredited researcher under the Digital Economy Act 2017?", + "lockedQuestion": 0, + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { "input": { - "options": [ - { - "conditionalQuestions": [ - { - "question": "If yes, please provide details", - "questionId": "safepeopleotherindividualsaccreditedresearcherdetails", - "input": { "type": "textareaInput" } - } - ], - "value": "Yes", - "text": "Yes" - }, - { "value": "No", "text": "No" } - ], - "type": "radioOptionsInput", - "label": "Is this person an accredited researcher under the Digital Economy Act 2017?" + "type": "textareaInput" }, - "question": "Is this person an accredited researcher under the Digital Economy Act 2017?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { - "label": "Has this person undertaken professional training or education on the topic of Information Governance?", - "type": "radioOptionsInput", - "options": [ - { - "text": "Yes", - "value": "Yes", - "conditionalQuestions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safepeopleotherindividualstraininginformationgovernancerecent", - "question": "Please provide full details regarding the most recent training" - } - ] - }, - { - "conditionalQuestions": [ - { - "questionId": "safepeopleotherindividualstraininginformationgovernanceintention", - "input": { "type": "textareaInput" }, - "question": "Please provide any details of plans to attend training, if applicable" - } - ], - "value": "No", - "text": "No" - } - ] + "question": "If yes, please provide your accredited researcher number.", + "questionId": "safepeopleprimaryapplicantaccreditedresearchernumber" + } + ] + }, + { + "conditionalQuestions": [ + { + "questionId": "safepeopleprimaryapplicantaccreditedresearcherintention", + "input": { + "type": "textareaInput" }, - "questionId": "safepeopleotherindividualstraininginformationgovernance", - "question": "Has this person undertaken professional training or education on the topic of Information Governance?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Please provide evidence of this person's expertise and experience relevant to delivering the project", - "input": { "type": "textareaInput" }, - "questionId": "safepeopleotherindividualsexperience", - "lockedQuestion": 1, - "defaultQuestion": 0 - } - ] + "question": "If no, please specify if you are planning to become an accredited researcher." + } + ], + "value": "No", + "text": "No" + } + ], + "type": "radioOptionsInput", + "label": "Are you an accredited researcher under the Digital Economy Act 2017?" + } }, { - "questionSetId": "add-safepeople-otherindividuals", - "questions": [ - { + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicanttraininginformationgovernance", + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.\n", + "question": "Have you undertaken professional training or education on the topic of Information Governance?", + "lockedQuestion": 0, + "input": { + "options": [ + { + "conditionalQuestions": [ + { "input": { - "class": "btn btn-primary addButton", - "type": "buttonInput", - "action": "addRepeatableSection", - "text": "Add another individual?", - "panelId": "safepeople-otherindividuals" + "type": "textareaInput" }, - "questionId": "add-safepeople-otherindividuals", - "lockedQuestion": 1, - "defaultQuestion": 0 - } - ] + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.\n", + "question": "Please provide full details regarding the most recent training", + "questionId": "safepeopleprimaryapplicanttraininginformationgovernancerecent" + } + ], + "value": "Yes", + "text": "Yes" + }, + { + "value": "No", + "conditionalQuestions": [ + { + "questionId": "safepeopleprimaryapplicanttraininginformationgovernanceintention", + "input": { + "type": "textareaInput" + }, + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian if training is required for your application.\n", + "question": "Please provide any details of plans to attend training, if applicable" + } + ], + "text": "No" + } + ], + "type": "radioOptionsInput", + "label": "Have you undertaken professional training or education on the topic of Information Governance?" + } }, { - "questionSetHeader": "About this application", - "questionSetId": "safeproject-aboutthisapplication", - "questions": [ - { - "question": "This application is...", - "guidance": "The application could be a new application, an extension, a renewal or amendment. For extensions or amendments, you must highlight the specific information within this form that has been updated, provide an original application number and approval date, any subsequent amendment approval dates and a summary of changes and rationale for the change to your original application and any updated approvals signatures in order for the request to be processed.", - "questionId": "safeprojectaboutthisapplicationtype", - "lockedQuestion": 1, - "defaultQuestion": 0, - "input": { - "options": [ - { "text": "A new application", "value": "A new application" }, - { - "conditionalQuestions": [ - { - "questionId": "safeprojectaboutthisapplicationtypeamendmentdetails", - "input": { "type": "textareaInput" }, - "question": "Reference or details of previous application" - } - ], - "text": "An amendment to an existing application", - "value": "An amendment to an existing application" - }, - { - "conditionalQuestions": [ - { - "question": "Reference or details of previous application", - "questionId": "safeprojectaboutthisapplicationtypeextensiondetails", - "input": { "type": "textareaInput" } - } - ], - "value": "An extension of an existing approval", - "text": "An extension of an existing approval" - }, - { - "conditionalQuestions": [ - { - "question": "Reference or details of previous application", - "questionId": "safeprojectaboutthisapplicationtyperenewaldetails", - "input": { "type": "textareaInput" } - } - ], - "text": "A renewal of an existing approval", - "value": "A renewal of an existing approval" - }, - { - "text": "Related to a previous application (approved or not)", - "value": "Related to a previous application (approved or not)", - "conditionalQuestions": [ - { - "question": "Reference or details of previous application", - "questionId": "safeprojectaboutthisapplicationtyperelatedpreviousdetails", - "input": { "type": "textareaInput" } - } - ] - } - ], - "label": "This application is...", - "type": "radioOptionsInput" - } - } - ] + "lockedQuestion": 1, + "input": { + "type": "textInput", + "required": true + }, + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "question": "Your organisation name", + "guidance": "Please give the full name of the organisation on whose behalf you are making the application or within which you work in your professional capacity as an applicant. This should include a parent organisation, and sub-division or department if appropriate (for example University of Edinburgh, Department of Informatics).", + "questionId": "safepeopleprimaryapplicantorganisationname", + "defaultQuestion": 1 }, { - "questions": [ - { - "guidance": "The title should identify the main area of your research so that another researcher could understand if it might be relevant to their area of study. \n \nThe titles of all Accredited Research projects are published on the UK Statistics Authority website as part of the public record of DEA Accredited Researchers.", - "validations": [{ "type": "isLength", "params": [{ "$numberDouble": "3.0" }, { "$numberDouble": "300.0" }] }], - "input": { "type": "textInput", "required": true }, - "questionId": "safeprojectprojectdetailstitle", - "question": "Title of project", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "What is the type of project?", - "guidance": "A research project is a discrete scientific endeavor to answer a research question or a set of research questions. \n\nA clinic audit project is designed and conducted to produce information to inform delivery of best care. It aims to find out if healthcare is being provided in line with standards to inform care providers and patients about where a service is doing well, and where there could be improvements.\n\nA service evaluation project is designed and conducted solely to define or judge current care. It seeks to assess current service to assess how well a service is achieving its intended aims.", - "questionId": "safeprojectprojectdetailstype", - "lockedQuestion": 1, - "defaultQuestion": 0, - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], - "input": { - "options": [ - { "value": "Research", "text": "Research" }, - { "value": "Clinic audit", "text": "Clinic audit" }, - { "value": "Service evaluation", "text": "Service evaluation" }, - { - "conditionalQuestions": [{ "question": "If other, please specify", "input": { "type": "textInput" }, "questionId": "" }], - "value": "Other", - "text": "Other" - } - ], - "type": "radioOptionsInput", - "required": true, - "label": "What is the type of project?" + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkit", + "guidance": "The Data Security and Protection Toolkit (DSPT) is an online self-assessment tool that allows organisations to measure their performance against the National Data Guardian’s 10 data security standards.\n\nAll organisations that have access to NHS patient data and systems must use the DSPT to provide assurance that they are practising good data security and that personal information is handled correctly.\n\nThe DSPT is an annual assessment.\n\nYou can find out the status of your organisation here https://www.dsptoolkit.nhs.uk/OrganisationSearch", + "question": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", + "lockedQuestion": 0, + "input": { + "options": [ + { + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitstatus", + "question": "If yes, please provide the current status", + "input": { + "type": "textInput" } - }, - { - "questionId": "safeprojectprojectdetailsneworexisting", - "lockedQuestion": 1, - "defaultQuestion": 0, - "input": { - "label": "Is this a new study or supporting an existing study?", - "type": "radioOptionsInput", - "options": [ - { "value": "New study", "text": "New study" }, - { - "text": "Existing study", - "value": "Existing study", - "conditionalQuestions": [ - { - "question": "Evidence of existing outputs", - "label": "Evidence of existing outputs", - "input": { - "options": [ - { - "value": "I have enclosed evidence of existing outputs", - "text": "I have enclosed evidence of existing outputs" - } - ], - "type": "checkboxOptionsInput" - }, - "questionId": "safeprojectprojectdetailsneworexistingexistingoutputevidence" - } - ] - } - ] + }, + { + "questionId": "safepeopleprimaryapplicantorganisationdatasecurityprotectionkitdatepublished", + "validations": [ + { + "type": "isCustomDate" + } + ], + "input": { + "type": "datePickerCustom" }, - "question": "Is this a new study or supporting an existing study?" - }, - { - "guidance": "Please provide a summary of the study in language suitable for non-experts in the field and ensure that all abbreviations in technical terminology are explained.\n \nThe summary must make clear what the specific purpose is, who will be using the data (organisations rather than individual names), what will happen to the data, whether the expected outputs are in record level form, what is known to date about your chosen project including any preliminary/related analysis and background literature reviews. Please include any potential disclosure risks and how these will be addressed.", - "validations": [{ "type": "isLength", "params": [{ "$numberDouble": "10.0" }, { "$numberDouble": "2000.0" }] }], - "input": { "required": true, "type": "textareaInput" }, - "questionId": "safeprojectprojectdetailslaysummary", - "question": "Please provide a lay summary of the project (300 words)", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "validations": [{ "type": "isCustomDate" }], - "input": { "type": "datePickerCustom" }, - "questionId": "safeproject-projectdetails-startdate", - "question": "What is the anticipated start date of the project?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Please provide anticipated end date of the project?", - "validations": [{ "type": "isCustomDate" }], - "input": { "type": "datePickerCustom" }, - "questionId": "safeproject-projectdetails-enddate", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "What are the project aims, objectives and rationale?", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], - "input": { "type": "textareaInput", "required": true }, - "questionId": "safeprojectprojectdetailsaimsobjectivesrationale", - "guidance": "Please include the background to the project by describing why you are conducting the study, the specific aims and the hypotheses that you hope to test. Summarise how the data requested are required to help address these aims. Please include whether the project has used peer review and if applicable the nature of that review. ", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { "type": "textareaInput", "required": true }, - "validations": [{ "type": "isLength", "message": "Please enter a value", "params": [{ "$numberDouble": "1.0" }] }], - "questionId": "safeprojectprojectdetailsdatausageforobjectives", - "guidance": "Provide full details of your research methodology. This must include justification of sample size, analyses proposed, statistical methods, additional data sources such as linked data and any plans for collaborative work. \n\nThis information will be key to assessing whether your proposal will be feasible, deliver clear public good and be an appropriate use of data. \n\nEnsure you: \n\nSpecify the method(s) of analysis you plan to use (such as regression);\n\nAs far as possible, try to articulate the outcome or dependent variable(s). \n\nIndicate the starting point for the modelling process - acknowledging that the model may evolve.\n\nExplain (where relevant) how any potential selection/causal bias will be addressed (e.g. by including a control group with information on how this control group will be created); \n\nProvide methodology references, if a non-standard methodology is proposed;\n\nInclude information about any contribution to the field of research methodology that you believe may result from your research;\n\nInclude an explanation of how your methodological approach will answer the research question(s) set out in the project when employing methods not covered by any of the above (e.g. correlation or basic descriptive analysis will only be used, noting that such analysis might be more applicable for exploratory research).", - "question": "How will the data requested be used to achieve the project objectives?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "How will your project benefit the public and what is the anticipated impact?", - "guidance": "Use this section to give the background and justification of your proposal, to demonstrate how your project will benefit the public, as well as show your understanding of the Information Governance issues specific and inherent to your project. Please make it clear how the data requested will contribute. \n\nPlease also show that you have considered how to balance the privacy risks and public benefits when designing the study. The requirement for the datasets requested should be fully justified in the light of the aims and objectives of the proposal.", - "input": { "required": true, "type": "textareaInput" }, - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], - "questionId": "safeprojectprojectdetailspublicbenefitimpact", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safeprojectprojectdetailspublicpatientinvolvementengagement", - "input": { "type": "textareaInput" }, - "guidance": "Provide full details of proposed public engagement plans for patient and/or user group involvement. If you have no plans, please elaborate why there will not be public engagement.", - "question": "Can you provide an outline of the public and patient involvement and engagement (PPIE*) strategies of the study or a brief explanation of why they are not planned?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Do you anticipate any risk to individual privacy, and if so what steps have you made in your proposal to mitigate these?", - "input": { "type": "textareaInput" }, - "questionId": "safeprojectprojectdetailsindividualprivacyriskmitigation", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { "type": "textareaInput" }, - "questionId": "safeprojectprojectdetailsgroupindividualdisavantageavoidance", - "question": "In considering your proposal, do you believe it could disadvantage any group or individual? Explain what steps you have taken to avoid this.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Please provide up to 6 keywords which best summarise your proposed research project.", - "input": { "type": "textareaInput" }, - "questionId": "safeprojectprojectdetailsresearchprojectsummarykeywords", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Will the research project enhance future benefit from our research database?", - "input": { "type": "textareaInput" }, - "questionId": "safeprojectprojectdetailsresearchprojectfuturedatabasebenefit", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "question": "If yes, please provide the date published" + } + ], + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } ], - "questionSetId": "safeproject-projectdetails", - "questionSetHeader": "Project details" - }, - { - "questions": [ - { - "question": "Does your project have a funder?", - "questionId": "safeprojectfunderinformationprojecthasfunder", - "lockedQuestion": 1, - "defaultQuestion": 0, - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please select an option", "type": "isLength" }], - "input": { - "label": "Does your project have a funder?", - "required": true, - "type": "radioOptionsInput", - "options": [ - { - "text": "Yes", - "value": "Yes", - "conditionalQuestions": [ - { - "question": "Please provide the organisation name", - "input": { "type": "textInput" }, - "questionId": "safeprojectfunderinformationprojecthasfundername" - }, - { - "questionId": "add-safeproject-funderinformation", - "input": { - "action": "addRepeatableQuestions", - "questionIds": ["safeprojectfunderinformationprojecthasfundername"], - "text": "Add another organisation?", - "separatorText": "Organisation Details", - "class": "btn btn-primary addButton", - "type": "buttonInput" - } - } - ] - }, - { - "text": "No", - "value": "No", - "conditionalQuestions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safeprojectfunderinformationprojecthasnofunderfundingsource", - "question": "If no, please provide details of how you intend to fund the study" - }, - { - "label": "Please provide evidence of independent peer review", - "input": { - "type": "checkboxOptionsInput", - "options": [ - { - "value": "I confirm I have provided evidence of independent peer review.", - "text": "I confirm I have provided evidence of independent peer review." - } - ] - }, - "questionId": "safeprojectfunderinformationprojecthasnofunderindependentpeerreviewevidence", - "question": "Please provide evidence of independent peer review" - } - ] - } - ] + "label": "Does your organisation have a current Data Security and Protection Toolkit (DSPT) published assessment?", + "type": "radioOptionsInput" + } + }, + { + "questionId": "safepeopleprimaryapplicantorganisationdatacontroller", + "defaultQuestion": 1, + "input": { + "options": [ + { + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safepeopleprimaryapplicantorganisationicoregisterednumber", + "question": "ICO registration number", + "input": { + "type": "textInput" } - } - ], - "questionSetHeader": "Funder information", - "questionSetId": "safeproject-funderinformation" - }, - { - "questions": [ - { - "question": "Does your project have a sponsor?", - "questionId": "safeprojectsponsorinformationprojecthassponsor", - "lockedQuestion": 1, - "defaultQuestion": 0, - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], - "input": { - "type": "radioOptionsInput", - "required": true, - "label": "Does your project have a sponsor?", - "options": [ - { - "conditionalQuestions": [ - { - "questionId": "safeprojectsponsorinformationprojecthassponsorapplicant", - "input": { "type": "textInput" }, - "question": "Associated applicant" - }, - { - "question": "Organisation name", - "input": { "type": "textInput" }, - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationname" - }, - { - "question": "Registered address (line 1)", - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", - "input": { "type": "textInput" } - }, - { - "question": "Registered address (line 2)", - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", - "input": { "type": "textInput" } - }, - { - "input": { "type": "textInput" }, - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcity", - "question": "City" - }, - { - "question": "Postcode", - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationpostcode", - "input": { "type": "textInput" } - }, - { - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcountry", - "input": { "type": "textInput" }, - "question": "Country" - }, - { - "question": "Sector", - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsector", - "input": { "type": "textInput" }, - "guidance": "Please provide details of the sponsor's sector e.g. NHS, Academia, Charity, Industry." - }, - { - "question": "Size", - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsize", - "input": { "type": "textInput" } - }, - { - "questionId": "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails", - "input": { "type": "textInput" }, - "question": "Additional details" - }, - { - "question": "Contact full name", - "questionId": "safeprojectsponsorinformationprojecthassponsorcontactfullname", - "input": { "type": "textInput" } - }, - { - "question": "Contact email address", - "input": { "type": "textInput" }, - "questionId": "safeprojectsponsorinformationprojecthassponsorcontactemail" - }, - { - "question": "Contact telephone", - "input": { "type": "textInput" }, - "questionId": "safeprojectsponsorinformationprojecthassponsorcontacttelephone" - }, - { - "questionId": "add-safeproject-sponsorinformation", - "input": { - "action": "addRepeatableQuestions", - "text": "Add another organisation?", - "questionIds": [ - "safeprojectsponsorinformationprojecthassponsorapplicant", - "safeprojectsponsorinformationprojecthassponsororganisationname", - "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", - "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", - "safeprojectsponsorinformationprojecthassponsororganisationcity", - "safeprojectsponsorinformationprojecthassponsororganisationpostcode", - "safeprojectsponsorinformationprojecthassponsororganisationcountry", - "safeprojectsponsorinformationprojecthassponsororganisationsector", - "safeprojectsponsorinformationprojecthassponsororganisationsize", - "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails", - "safeprojectsponsorinformationprojecthassponsorcontactfullname", - "safeprojectsponsorinformationprojecthassponsorcontactemail", - "safeprojectsponsorinformationprojecthassponsorcontacttelephone" - ], - "separatorText": "Organisation Details", - "class": "btn btn-primary addButton", - "type": "buttonInput" - } - } - ], - "text": "Yes", - "value": "Yes" - }, - { "value": "No", "text": "No" } - ] + }, + { + "questionId": "safepeopleprimaryapplicantorganisationaddressline1", + "guidance": "Please include the organisation's business address.", + "question": "Registered address (line 1)", + "input": { + "type": "textInput" } - } - ], - "questionSetHeader": "Sponsor information", - "questionSetId": "safeproject-sponsorinformation" - }, - { - "questions": [ - { - "question": "Is there a commercial interest in this project?", - "questionId": "safeprojectdeclarationofinteresthascommercialinterest", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], - "lockedQuestion": 1, - "defaultQuestion": 0, - "input": { - "label": "Is there a commercial interest in this project?", - "required": true, - "type": "radioOptionsInput", - "options": [ - { - "value": "Yes", - "text": "Yes", - "conditionalQuestions": [ - { - "question": "Organisation name", - "input": { "type": "textInput" }, - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationname" - }, - { - "question": "Registered address (line 1)", - "input": { "type": "textInput" }, - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1" - }, - { - "input": { "type": "textInput" }, - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", - "question": "Registered address (line 2)" - }, - { - "question": "Postcode", - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", - "input": { "type": "textInput" } - }, - { - "question": "City", - "input": { "type": "textInput" }, - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcity" - }, - { - "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry", - "input": { "type": "textInput" }, - "question": "Country" - }, - { - "question": "Describe the nature of interest", - "questionId": "safeprojectdeclarationofinteresthascommercialinterestinterestnature", - "input": { "type": "textareaInput" } - }, - { - "label": "Public interest", - "questionId": "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest", - "input": { - "options": [ - { - "value": "I confirm that any commercial interest is public interest related.", - "text": "I confirm that any commercial interest is public interest related." - } - ], - "type": "checkboxOptionsInput" - }, - "question": "Public interest" - }, - { - "input": { - "questionIds": [ - "safeprojectdeclarationofinteresthascommercialinterestorganisationname", - "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1", - "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", - "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", - "safeprojectdeclarationofinteresthascommercialinterestorganisationcity", - "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry", - "safeprojectdeclarationofinteresthascommercialinterestinterestnature", - "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest" - ], - "text": "Add another organisation?", - "action": "addRepeatableQuestions", - "type": "buttonInput", - "class": "btn btn-primary addButton", - "separatorText": "Organisation Details" - }, - "questionId": "add-safeproject-declarationofinterest" - } - ] - }, - { "value": "No", "text": "No" } - ] + }, + { + "input": { + "type": "textInput" + }, + "question": "Registered address (line 2)", + "guidance": "Please include the organisation's business address.", + "questionId": "safepeopleprimaryapplicantorganisationaddressline2" + }, + { + "guidance": "Please specify the city where the organisation is located", + "question": "City", + "input": { + "type": "textInput" + }, + "questionId": "safepeopleprimaryapplicantorganisationcity" + }, + { + "questionId": "safepeopleprimaryapplicantorganisationpostcode", + "input": { + "type": "textInput" + }, + "guidance": "Please include the organisation's business address postcode", + "question": "Postcode" + }, + { + "questionId": "safepeopleprimaryapplicantorganisationcountry", + "input": { + "type": "textInput" + }, + "question": "Country", + "guidance": "Please specify the country where the organisation is located." + }, + { + "questionId": "safepeopleprimaryapplicantorganisationtype", + "label": "Organisation type", + "guidance": "Please select type of organisation.", + "question": "Organisation type", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "text": "Academic institution", + "value": "Academic institution" + }, + { + "value": "National body", + "text": "National body" + }, + { + "value": "Healthcare provider", + "text": "Healthcare provider" + }, + { + "value": "Healthcare comissioner", + "text": "Healthcare comissioner" + }, + { + "text": "Commercial body", + "value": "Commercial body" + }, + { + "text": "Local Authority", + "value": "Local Authority" + }, + { + "value": "Other", + "text": "Other" + } + ] } - } + } + ], + "text": "Yes" + }, + { + "text": "No", + "value": "No" + }, + { + "value": "Unsure", + "text": "Unsure" + } ], - "questionSetHeader": "Declaration of interest", - "questionSetId": "safeproject-declarationofinterest" - }, - { - "questionSetHeader": "Intellectual property", - "questionSetId": "safeproject-intellectualproperty", - "questions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safeprojectintellectualpropertydevelopmentnewproductprocess", - "question": "Please indicate if the research could lead to the development of a new product/process or the generation of intellectual property.", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "type": "radioOptionsInput", + "label": "Will your organisation act as data controller?" + }, + "lockedQuestion": 0, + "guidance": "Please specify if your organisation will act as a data controller. If your organisation is not the sole data controller, please provide details of other data controllers.", + "question": "Will your organisation act as data controller?" + }, + { + "question": "CV", + "guidance": "Documents can be uploaded in the Additional Files section of this form.", + "lockedQuestion": 0, + "input": { + "label": "CV", + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I have uploaded a CV for this person", + "text": "I have uploaded a CV for this person" + } ] + }, + "defaultQuestion": 1, + "questionId": "safepeopleprimaryapplicantuploadedcv" }, { - "questionSetId": "safedata-datafields", - "questionSetHeader": "Data fields", - "questions": [ - { - "questionId": "safedatadatafieldsdatarequiredjustification", - "input": { "type": "textareaInput" }, - "question": "Please indicate the data necessary to conduct the study, the data fields required and the justifications for each field.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safedatadatafieldsfileuploaded", - "input": { - "options": [ - { - "value": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field.", - "text": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field." - } - ], - "label": "Data fields indicated via file upload", - "type": "checkboxOptionsInput" - }, - "question": "Data fields indicated via file upload", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "guidance": "A description of precisely the criteria which define the patients to be included and to be excluded from the data extract you are requesting should be provided. \n\nThis should include precise date parameters for the start and end of the range requested (dd/mm/yy) and explain which dated project field will be used to define the requested cohort (e.g. date of admission or date of operation).", - "questionId": "safedatadatafieldsinclusionexclusioncriteria", - "validations": [{ "type": "isLength", "message": "Please enter a value", "params": [{ "$numberDouble": "1.0" }] }], - "input": { "type": "textareaInput", "required": true }, - "question": "Inclusion and exclusion criteria (including date parameters)", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Will you require periodic refreshes of the data?", - "questionId": "safedatadatafieldsdatarefreshrequired", - "validations": [{ "message": "Please select an option", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], - "input": { - "label": "Will you require periodic refreshes of the data?", - "type": "radioOptionsInput", - "required": true, - "options": [ - { - "conditionalQuestions": [ - { - "guidance": "Please indicate how often data refreshes will be needed.", - "questionId": "safedatadatafieldsdatarefreshrequiredperiodicity", - "input": { - "label": "How often will the data refreshes be needed?", - "type": "radioOptionsInput", - "options": [ - { "text": "Every month", "value": "Every month" }, - { "text": "Every 3 months", "value": "Every 3 months" }, - { "text": "Every 6 months", "value": "Every 6 months" }, - { "text": "Every 12 months", "value": "Every 12 months" }, - { - "text": "Other", - "value": "Other", - "conditionalQuestions": [ - { - "questionId": "safedatadatafieldsdatarefreshrequiredperiodicityotherdetails", - "input": { "type": "textInput" }, - "question": "If other, please specify" - } - ] - } - ] - }, - "question": "How often will the data refreshes be needed?" - } - ], - "value": "Yes", - "text": "Yes" - }, - { "value": "No", "text": "No" } - ] + "questionId": "safepeopleprimaryapplicantexperience", + "defaultQuestion": 1, + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "Please provide evidence of the primary applicant's expertise and experience relevant to delivering the project", + "guidance": "You must be able to demonstrate the team are qualified / experienced enough to work on the project. Applicants should provide qualifications as bona fide researcher(s) experienced in the use of large research datasets and provide evidence of their experience in their research field. " + } + ] + }, + { + "questionSetId": "safepeople-otherindividuals", + "questionSetHeader": "Other individuals", + "questions": [ + { + "questionId": "safepeopleotherindividualsfullname", + "defaultQuestion": 1, + "input": { + "type": "textInput" + }, + "lockedQuestion": 1, + "question": "Full name", + "guidance": "Full name is the individual's first and last name" + }, + { + "defaultQuestion": 1, + "questionId": "safepeopleotherindividualsjobtitle", + "guidance": "Job Title is the name of the position the individual holds within their organisation.", + "question": "Job title", + "input": { + "type": "textInput" + }, + "lockedQuestion": 1 + }, + { + "defaultQuestion": 1, + "questionId": "safepeopleotherindividualsorcid", + "guidance": "ORCID provides a persistent digital identifier (an ORCID iD) that you own and control, and that distinguishes you from every other researcher. You can create an ORCID profile at https://orcid.org/. If your collaborator has an ORCID iD please include it here.", + "question": "ORCID", + "lockedQuestion": 0, + "input": { + "type": "textInput" + } + }, + { + "questionId": "safepeopleotherindividualsorganisation", + "defaultQuestion": 1, + "input": { + "type": "textInput" + }, + "lockedQuestion": 0, + "guidance": "Please include the individual's organisation.", + "question": "Organisation" + }, + { + "questionId": "safepeopleotherindividualsrole", + "defaultQuestion": 1, + "input": { + "type": "checkboxOptionsInput", + "label": "Role", + "options": [ + { + "value": "Principal investigator", + "text": "Principal investigator" + }, + { + "text": "Collaborator", + "value": "Collaborator" + }, + { + "text": "Team member", + "conditionalQuestions": [ + { + "input": { + "type": "textareaInput" }, - "guidance": "Please indicate if data refreshes will be required.", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "question": "If other, please specify", + "questionId": "safepeopleotherindividualsroleotherdetails" + } + ], + "value": "Team member" + }, + { + "text": "Other", + "value": "Other" + } ] + }, + "lockedQuestion": 0, + "guidance": "A role is a function that the applicant plays. It might include role types and accreditation for those that are accessing the secure data and those that are not but would see cleared outputs from the project. \n (i.e. project lead, deputy lead, accrediter, researcher, peer reviewer)\n", + "question": "Role" }, { - "questions": [ - { + "guidance": "Please confirm whether this person will be accessing the resources being requested.", + "question": "Will this person access the data requested?", + "lockedQuestion": 1, + "input": { + "options": [ + { + "value": "Yes", + "text": "Yes", + "conditionalQuestions": [ + { + "questionId": "safepeopleotherindividualsaccreditedresearcherdetails", + "question": "If yes, please provide details", "input": { - "label": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", - "type": "radioOptionsInput", - "required": true, - "options": [ - { - "text": "Yes", - "value": "Yes", - "conditionalQuestions": [ - { - "input": { "type": "textareaInput" }, - "questionId": "safedataotherdatasetslinkadditionaldatasetslinkagedetails", - "question": "Specify all datasets, organisations which will perform the linkage and how the linkage will take place." - } - ] - }, - { "value": "No", "text": "No" } - ] + "type": "textareaInput" + } + } + ] + }, + { + "value": "No", + "text": "No" + } + ], + "type": "radioOptionsInput", + "label": "Will this person access the data requested?" + }, + "defaultQuestion": 1, + "questionId": "safepeopleotherindividualsaccessdata" + }, + { + "guidance": "Please confirm whether this person is an accredited researcher under the Digital Economy Act 2017 (https://www.legislation.gov.uk/ukpga/2017/30/contents/enacted).", + "question": "Is this person an accredited researcher under the Digital Economy Act 2017?", + "lockedQuestion": 0, + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safepeopleotherindividualstraininginformationgovernancerecent", + "input": { + "type": "textareaInput" }, - "validations": [{ "type": "isLength", "message": "Please select an option", "params": [{ "$numberDouble": "1.0" }] }], - "questionId": "safedataotherdatasetslinkadditionaldatasets", - "question": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safedataotherdatasetsrisksmitigations", - "input": { "type": "textareaInput" }, - "question": "Please summarise the risks/mitigations considered.", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "question": "Please provide full details regarding the most recent training" + } + ] + }, + { + "text": "No", + "value": "No" + } ], - "questionSetId": "safedata-otherdatasetsintentiontolinkdata", - "questionSetHeader": "Other datasets - Intention to link data" + "label": "Is this person an accredited researcher under the Digital Economy Act 2017?", + "type": "radioOptionsInput" + }, + "defaultQuestion": 1, + "questionId": "safepeopleotherindividualsaccreditedresearcher" }, { - "questions": [ - { - "guidance": "The lawful basis for processing are set out in Article 6 of the GDPR. At least one legal basis must apply whenever you process personal data. Please select appropriate Article 6 lawful basis. Processing shall be lawful only if and to the extent that at least one of the following applies.", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], + "questionId": "safepeopleotherindividualstraininginformationgovernance", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safepeopleotherindividualstraininginformationgovernanceintention", + "question": "Please provide any details of plans to attend training, if applicable", "input": { - "required": true, - "type": "radioOptionsInput", - "label": "Article 6 lawful basis", - "options": [ - { "text": "Not applicable", "value": "Not applicable" }, - { - "value": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;", - "text": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;" - }, - { - "value": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;", - "text": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;" - }, - { - "value": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;", - "text": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;" - }, - { - "value": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;", - "text": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;" - }, - { - "value": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;", - "text": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;" - }, - { - "value": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child.", - "text": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child." - } - ] + "type": "textareaInput" + } + } + ] + }, + { + "value": "No", + "text": "No" + } + ], + "label": "Has this person undertaken professional training or education on the topic of Information Governance?", + "type": "radioOptionsInput" + }, + "question": "Has this person undertaken professional training or education on the topic of Information Governance?", + "guidance": "Evidence of Information Governance training is an important aspect of most applications, giving assurance that individuals are aware of the privacy, confidentiality, data protection and Caldicott implications of working with personal data. \n\nPlease ensure you have checked with the data custodian what training is required for your application." + }, + { + "input": { + "label": "CV", + "type": "checkboxOptionsInput", + "options": [ + { + "text": "I have uploaded a CV for this person", + "value": "I have uploaded a CV for this person" + } + ] + }, + "lockedQuestion": 0, + "question": "CV", + "guidance": "Documents can be uploaded in the Additional Files section of this form.", + "questionId": "safepeopleotherindividualsuploadedcv", + "defaultQuestion": 1 + }, + { + "defaultQuestion": 1, + "questionId": "safepeopleotherindividualsexperience", + "guidance": "You must be able to demonstrate the team are qualified / experienced enough to work on the project. Applicants should provide qualifications as bona fide researcher(s) experienced in the use of large research datasets and provide evidence of their experience in their research field. ", + "question": "Please provide evidence of this person's expertise and experience relevant to delivering the project", + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + } + } + ] + }, + { + "questions": [ + { + "lockedQuestion": 0, + "input": { + "panelId": "safepeople-otherindividuals", + "class": "btn btn-primary addButton", + "type": "buttonInput", + "text": "Add another individual?", + "action": "addRepeatableSection" + }, + "questionId": "add-safepeople-otherindividuals", + "defaultQuestion": 1 + } + ], + "questionSetId": "add-safepeople-otherindividuals" + }, + { + "questions": [ + { + "input": { + "options": [ + { + "value": "A new application", + "text": "A new application" + }, + { + "text": "An amendment to an existing application", + "conditionalQuestions": [ + { + "input": { + "type": "textareaInput" }, - "questionId": "safedatalawfulbasisgdprarticle6basis", - "question": "Article 6 lawful basis", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "guidance": "Please provide justification for selected Article 6 lawful basis.", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], - "input": { "type": "textareaInput", "required": true }, - "questionId": "safedatalawfulbasisgdprarticle6basisjustification", - "question": "Article 6 legal basis justification", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "guidance": "Please select appropriate Article 9 conditions. \n \nProcessing of personal data revealing racial or ethnic origin, political opinions, religious or philosophical beliefs, or trade union membership, and the processing of genetic data, biometric data for the purpose of uniquely identifying a natural person, data concerning health or data concerning a natural person's sex life or sexual orientation shall be prohibited. This does not apply if one of the following applies.", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please select an option" }], - "questionId": "safedatalawfulbasisgdprarticle9conditions", - "input": { - "label": "Article 9 conditions", - "type": "radioOptionsInput", - "required": true, - "options": [ - { "text": "Not applicable", "value": "Not applicable" }, - { - "value": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;", - "text": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;" - }, - { - "value": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;", - "text": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;" - }, - { - "text": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;", - "value": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;" - }, - { - "text": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;", - "value": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;" - }, - { - "text": "(e) processing relates to personal data which are manifestly made public by the data subject;", - "value": "(e) processing relates to personal data which are manifestly made public by the data subject;" - }, - { - "text": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;", - "value": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;" - }, - { - "text": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;", - "value": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;" - }, - { - "text": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;", - "value": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;" - }, - { - "value": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;", - "text": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;" - }, + "question": "Reference or details of previous application", + "guidance": "Please provide the UK Health Research Authority Research Ethics Service reference. If not a research study please provide references or details of previous application. ", + "questionId": "safeprojectaboutthisapplicationtypeamendmentdetails" + } + ], + "value": "An amendment to an existing application" + }, + { + "text": "An extension of an existing approval", + "value": "An extension of an existing approval", + "conditionalQuestions": [ + { + "questionId": "safeprojectaboutthisapplicationtypeextensiondetails", + "question": "Reference or details of previous application", + "guidance": "Please provide the UK Health Research Authority Research Ethics Service reference. If not a research study please provide references or details of previous application. ", + "input": { + "type": "textareaInput" + } + } + ] + }, + { + "text": "A renewal of an existing approval", + "value": "A renewal of an existing approval", + "conditionalQuestions": [ + { + "input": { + "type": "textareaInput" + }, + "guidance": "Please provide the UK Health Research Authority Research Ethics Service reference. If not a research study please provide references or details of previous application. ", + "question": "Reference or details of previous application", + "questionId": "safeprojectaboutthisapplicationtyperenewaldetails" + } + ] + }, + { + "value": "Related to a previous application (approved or not)", + "conditionalQuestions": [ + { + "questionId": "safeprojectaboutthisapplicationtyperelatedpreviousdetails", + "question": "Reference or details of previous application", + "guidance": "Please provide the UK Health Research Authority Research Ethics Service reference. If not a research study please provide references or details of previous application. ", + "input": { + "type": "textareaInput" + } + } + ], + "text": "Related to a previous application (approved or not)" + } + ], + "label": "This application is...", + "type": "radioOptionsInput" + }, + "lockedQuestion": 1, + "guidance": "The application could be a new application, an extension, a renewal or amendment. For extensions or amendments, you must highlight the specific information within this form that has been updated, provide an original application number and approval date, any subsequent amendment approval dates and a summary of changes and rationale for the change to your original application and updated approvals signatures in order for the request to be processed.", + "question": "This application is...", + "questionId": "safeprojectaboutthisapplicationtype", + "defaultQuestion": 1 + } + ], + "questionSetId": "safeproject-aboutthisapplication", + "questionSetHeader": "About this application" + }, + { + "questions": [ + { + "defaultQuestion": 1, + "guidance": "The title should identify the main area of your research so that another researcher could understand if it might be relevant to their area of study. \n", + "lockedQuestion": 1, + "validations": [ + { + "type": "isLength", + "params": [ + 3, + 300 + ] + } + ], + "questionId": "safeprojectprojectdetailstitle", + "question": "Title of project", + "input": { + "type": "textInput", + "required": true + } + }, + { + "input": { + "label": "What is the type of project?", + "type": "radioOptionsInput", + "required": true, + "options": [ + { + "value": "Research", + "text": "Research" + }, + { + "text": "Clinic audit", + "value": "Clinic audit" + }, + { + "value": "Service evaluation", + "text": "Service evaluation" + }, + { + "value": "Other", + "conditionalQuestions": [ + { + "questionId": "safeprojectprojectdetailstypeotherdetails", + "question": "If other, please specify", + "input": { + "type": "textInput" + } + } + ], + "text": "Other" + } + ] + }, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please select an option" + } + ], + "lockedQuestion": 1, + "guidance": "A research project is a discrete scientific endeavour to answer a research question or a set of research questions. \n\nA clinic audit project is designed and conducted to produce information to inform delivery of best care. It aims to find out if healthcare is being provided in line with standards to inform care providers and patients about where a service is doing well, and where there could be improvements.\n\nA service evaluation project is designed and conducted solely to define or judge current care. It seeks to assess current service to assess how well a service is achieving its intended aims.", + "question": "What is the type of project?", + "questionId": "safeprojectprojectdetailstype", + "defaultQuestion": 1 + }, + { + "lockedQuestion": 0, + "input": { + "label": "Is this a new study or supporting an existing project?", + "type": "radioOptionsInput", + "options": [ + { + "text": "New project", + "value": "New project" + }, + { + "conditionalQuestions": [ + { + "question": "Evidence of existing outputs", + "input": { + "options": [ + { + "value": "I have enclosed evidence of existing outputs", + "text": "I have enclosed evidence of existing outputs" + } + ], + "type": "checkboxOptionsInput" + }, + "questionId": "safeprojectprojectdetailsneworexistingexistingoutputevidence", + "label": "Evidence of existing outputs" + } + ], + "value": "Existing project", + "text": "Existing project" + } + ] + }, + "question": "Is this a new study or supporting an existing project?", + "questionId": "safeprojectprojectdetailsneworexisting", + "defaultQuestion": 1, + "guidance": "" + }, + { + "question": "Has the hypothesis being investigated been commissioned by the NHS?", + "lockedQuestion": 0, + "input": { + "label": "Has the hypothesis being investigated been commissioned by the NHS?", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "conditionalQuestions": [ + { + "question": "If yes, please provide details of the commission and any peer review to date. (100 words)", + "validations": [ + { + "params": [ + 10, + 700 + ], + "type": "isLength" + } + ], + "input": { + "type": "textareaInput" + }, + "questionId": "safeprojectprojectdetailshypothesiscomissionednhsdetails" + } + ], + "value": "Yes" + }, + { + "text": "No", + "value": "No" + } + ] + }, + "defaultQuestion": 1, + "questionId": "safeprojectprojectdetailshypothesiscomissionednhs", + "guidance": "" + }, + { + "defaultQuestion": 1, + "guidance": "Please provide a summary of the project in language suitable for non-experts in the field and ensure that all abbreviations in technical terminology are explained. Do not include any confidential or sensitive information in this section as this section may be used to publicise the project if the application is successful.\n\nThe summary must make clear what the specific purpose is, who will be using the data (organisations rather than individual names), what will happen to the data, whether the expected outputs are in record level form, what is known to date about your chosen project including any preliminary/related analysis and background literature reviews. Please include any potential disclosure risks and how these will be addressed.", + "lockedQuestion": 1, + "validations": [ + { + "type": "isLength", + "params": [ + 10, + 2000 + ] + } + ], + "questionId": "safeprojectprojectdetailslaysummary", + "question": "Please provide a lay summary of the project (300 words)", + "input": { + "type": "textareaInput", + "required": true + } + }, + { + "questionId": "safeproject-projectdetails-startdate", + "defaultQuestion": 1, + "input": { + "type": "datePickerCustom" + }, + "validations": [ + { + "type": "isCustomDate" + } + ], + "lockedQuestion": 0, + "question": "What is the anticipated start date of the project?", + "guidance": "" + }, + { + "question": "Please provide anticipated end date of the project?", + "lockedQuestion": 0, + "input": { + "type": "datePickerCustom" + }, + "validations": [ + { + "type": "isCustomDate" + } + ], + "defaultQuestion": 1, + "questionId": "safeproject-projectdetails-enddate", + "guidance": "" + }, + { + "input": { + "type": "textareaInput", + "required": true + }, + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "lockedQuestion": 1, + "guidance": "Please include background to the project, including why the project is being undertaken and the specific aims and the hypotheses being tested. Summarise how the data requested are required to help address these aims. Please include whether any aspect of the project has been peer reviewed and if applicable, the nature of that review. ", + "question": "What are the project aims, objectives and rationale?", + "questionId": "safeprojectprojectdetailsaimsobjectivesrationale", + "defaultQuestion": 1 + }, + { + "questionId": "safeprojectprojectdetailsdatausageforobjectives", + "defaultQuestion": 1, + "lockedQuestion": 1, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "input": { + "required": true, + "type": "textareaInput" + }, + "guidance": "Provide full details of your research methodology. This must include justification of sample size, analyses proposed, statistical methods, additional data sources such as linked data and any plans for collaborative work. \n\nThis information will be key to assessing whether your proposal will be feasible, deliver clear public good and be an appropriate use of data. \n\nEnsure you: \n\nSpecify the method(s) of analysis you plan to use (such as regression);\n\nAs far as possible, try to articulate the outcome or dependent variable(s). \n\nIndicate the starting point for the modelling process - acknowledging that the model may evolve.\n\nExplain (where relevant) how any potential selection/causal bias will be addressed (e.g. by including a control group with information on how this control group will be created); \n\nProvide methodology references, if a non-standard methodology is proposed;\n\nInclude information about any contribution to the field of research methodology that you believe may result from your research;\n\nInclude an explanation of how your methodological approach will answer the research question(s) set out in the project when employing methods not covered by any of the above (e.g. correlation or basic descriptive analysis will only be used, noting that such analysis might be more applicable for exploratory research).", + "question": "How will the data requested be used to achieve the project objectives?" + }, + { + "questionId": "safeprojectprojectdetailspublicbenefitimpacttype", + "defaultQuestion": 1, + "lockedQuestion": 1, + "input": { + "options": [ + { + "value": "Further understanding of the health and care needs of populations", + "text": "Further understanding of the health and care needs of populations" + }, + { + "text": "Lead to the identification and progress of treatments and therapies to improve health", + "value": "Lead to the identification and progress of treatments and therapies to improve health" + }, + { + "text": "Further understanding of regional and national trends in health and social care needs", + "value": "Further understanding of regional and national trends in health and social care needs" + }, + { + "text": "Inform planning health services and programmes", + "value": "Inform planning health services and programmes" + }, + { + "text": "Inform decisions on how to effectively allocate and evaluate funding according to health needs?", + "value": "Inform decisions on how to effectively allocate and evaluate funding according to health needs?" + }, + { + "text": "Further knowledge creation or support exploratory research and the innovations and developments that might result from that exploratory work.", + "value": "Further knowledge creation or support exploratory research and the innovations and developments that might result from that exploratory work." + }, + { + "value": "Other", + "text": "Other" + } + ], + "type": "checkboxOptionsInput", + "label": "How will you demonstrate that use of the data requested will deliver public benefit? Please select all that apply." + }, + "question": "How will you demonstrate that use of the data requested will deliver public benefit? Please select all that apply.", + "guidance": "The concept of public benefit is broad and flexible, and could include direct, indirect and long-term benefits. However, the benefit needs to be identifiable regardless of whether it can be quantified or measured. Please select at least one of the options provided to indicate how you would determine whether the use of requested data is for public benefit.\n\n---\n\n\n**This guidance is aligned with: ** \n\n\n![Test image](https://storage.googleapis.com/hdruk-gateway_non-prod-cms/web-assets/NDG-logo-small.png)" + }, + { + "guidance": "Use this section to give the background and justification of your proposal, to demonstrate how your project will benefit the public, as well as show your understanding of the Information Governance issues specific and inherent to your project. Please make it clear how the data requested will contribute. \n\nPlease also show that you have considered how to balance the privacy risks and public benefits when designing the study. The requirement for the datasets requested should be fully justified in the light of the aims and objectives of the proposal.\n", + "question": "Please provide details to justify your selection above.", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "safeprojectprojectdetailspublicbenefitimpact" + }, + { + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "Provide an outline of the public and patient involvement and engagement (PPIE*) strategy for the study, OR a brief explanation as to why this is not required.", + "guidance": "Provide full details of proposed public engagement plans for patient and/or user group involvement. If you have no public engagement plans, please explain why this is the case.", + "questionId": "safeprojectprojectdetailspublicpatientinvolvementengagement", + "defaultQuestion": 1 + }, + { + "defaultQuestion": 1, + "questionId": "safeprojectprojectdetailsindividualprivacyriskmitigation", + "question": "Do you anticipate any risk to individuals, and if so what steps have you made in your proposal to mitigate these?", + "guidance": "A data use purpose cannot be considered as beneficial if the harm resulting from the use of the data outweighs the public benefit. Please demonstrate that you have considered how to balance the risks and public benefits when designing the study.\n\nPlease consider:\n\n- Could individual patient privacy be compromised?\n- Could patient or service user safety be harmed? \n- If data was to be used for this purpose, could it make some patients or service users less likely to seek care or be less frank in discussion with health and care professionals? \n- Could the purpose lead to the creation or exacerbation of inequalities or unlawful discrimination against particular communities? \n- Could the use of inaccurate or inadequate health and social care data lead to unrepresentative findings? \n- Could the purpose undermine the sustainability of publicly funded health or adult social care services? \n\n---\n\n\n**This guidance is aligned with: ** \n\n\n![Test image](https://storage.googleapis.com/hdruk-gateway_non-prod-cms/web-assets/NDG-logo-small.png)", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 1 + }, + { + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "question": "In considering your proposal, do you believe it could disadvantage any group or individual? Explain what steps you have taken to avoid this.", + "questionId": "safeprojectprojectdetailsgroupindividualdisavantageavoidance", + "defaultQuestion": 1, + "guidance": "" + }, + { + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "Please provide up to 6 keywords which best summarise your proposed research project.", + "questionId": "safeprojectprojectdetailsresearchprojectsummarykeywords", + "defaultQuestion": 1, + "guidance": "" + }, + { + "lockedQuestion": 0, + "input": { + "label": "Please indicate whether the proposed project will enhance future benefits of our dataset, and describe any expected benefits", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safeprojectprojectdetailsresearchprojectfuturedatabasebenefit", + "question": "If yes, please provide details", + "input": { + "type": "textareaInput" + } + } + ] + }, + { + "text": "No", + "value": "No" + } + ] + }, + "question": "Please indicate whether the proposed project will enhance future benefits of our dataset, and describe any expected benefits", + "questionId": "safeprojectprojectdetailsresearchprojectfuturedatabasebenefityesorno", + "defaultQuestion": 1, + "guidance": "" + } + ], + "questionSetHeader": "Project details", + "questionSetId": "safeproject-projectdetails" + }, + { + "questionSetHeader": "Funder information", + "questionSetId": "safeproject-funderinformation", + "questions": [ + { + "lockedQuestion": 1, + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safeprojectfunderinformationprojecthasfundername", + "input": { + "type": "textInput" + }, + "guidance": "Please confirm funder organisation name.", + "question": "Please provide the organisation name" + }, + { + "questionId": "add-safeproject-funderinformation", + "input": { + "text": "Add another organisation?", + "action": "addRepeatableQuestions", + "class": "btn btn-primary addButton", + "separatorText": "Organisation Details", + "type": "buttonInput", + "questionIds": [ + "safeprojectfunderinformationprojecthasfundername" + ] + } + } + ] + }, + { + "value": "No", + "conditionalQuestions": [ + { + "questionId": "safeprojectfunderinformationprojecthasnofunderfundingsource", + "input": { + "type": "textareaInput" + }, + "question": "If no, please provide details of how you intend to fund the study" + }, + { + "question": "Please provide evidence of independent peer review", + "guidance": "Documents can be uploaded in the Additional Files section of this form.", + "input": { + "options": [ + { + "text": "I confirm I have provided evidence of independent peer review.", + "value": "I confirm I have provided evidence of independent peer review." + } + ], + "type": "checkboxOptionsInput" + }, + "label": "Please provide evidence of independent peer review", + "questionId": "safeprojectfunderinformationprojecthasnofunderindependentpeerreviewevidence" + } + ], + "text": "No" + } + ], + "required": true, + "type": "radioOptionsInput", + "label": "Does your project have a funder?" + }, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please select an option" + } + ], + "question": "Does your project have a funder?", + "guidance": "Please confirm if your project has a funder.", + "questionId": "safeprojectfunderinformationprojecthasfunder", + "defaultQuestion": 1 + } + ] + }, + { + "questionSetId": "safeproject-sponsorinformation", + "questionSetHeader": "Sponsor information", + "questions": [ + { + "guidance": "Please confirm if your project has a sponsor.", + "question": "Does your project have a sponsor?", + "input": { + "required": true, + "options": [ + { + "conditionalQuestions": [ + { + "question": "Associated applicant", + "input": { + "type": "textInput" + }, + "questionId": "safeprojectsponsorinformationprojecthassponsorapplicant" + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationname", + "question": "Organisation name", + "guidance": "Please provide legal name; to appear on legal documents.", + "input": { + "type": "textInput" + } + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", + "input": { + "type": "textInput" + }, + "guidance": "Please confirm sponsor organisation address.", + "question": "Registered address (line 1)" + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", + "input": { + "type": "textInput" + }, + "guidance": "Please confirm sponsor organisation address.", + "question": "Registered address (line 2)" + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcity", + "input": { + "type": "textInput" + }, + "question": "City", + "guidance": "Please confirm sponsor organisation city." + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationpostcode", + "question": "Postcode", + "guidance": "Please confirm sponsor organisation postcode.", + "input": { + "type": "textInput" + } + }, + { + "question": "Country", + "guidance": "Please confirm sponsor organisation's country.", + "input": { + "type": "textInput" + }, + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationcountry" + }, + { + "question": "Sector", + "guidance": "Please provide details of the sponsor's sector e.g. NHS, Academia, Charity, Industry.", + "input": { + "type": "textInput" + }, + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsector" + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationsize", + "guidance": "Please specify the size of the organisation (small, medium, large).", + "question": "Size", + "input": { + "type": "textInput" + } + }, + { + "input": { + "type": "textInput" + }, + "guidance": "Please provide additional details, if applicable.", + "question": "Additional details", + "questionId": "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails" + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsorcontactfullname", + "question": "Contact full name", + "input": { + "type": "textInput" + } + }, + { + "questionId": "safeprojectsponsorinformationprojecthassponsorcontactemail", + "question": "Contact email address", + "guidance": "Please provide a contact email address for the sponsor organisation", + "input": { + "type": "textInput" + } + }, + { + "input": { + "type": "textInput" + }, + "question": "Contact telephone", + "questionId": "safeprojectsponsorinformationprojecthassponsorcontacttelephone" + }, + { + "questionId": "add-safeproject-sponsorinformation", + "input": { + "class": "btn btn-primary addButton", + "action": "addRepeatableQuestions", + "text": "Add another organisation?", + "separatorText": "Organisation Details", + "questionIds": [ + "safeprojectsponsorinformationprojecthassponsorapplicant", + "safeprojectsponsorinformationprojecthassponsororganisationname", + "safeprojectsponsorinformationprojecthassponsororganisationaddressline1", + "safeprojectsponsorinformationprojecthassponsororganisationaddressline2", + "safeprojectsponsorinformationprojecthassponsororganisationcity", + "safeprojectsponsorinformationprojecthassponsororganisationpostcode", + "safeprojectsponsorinformationprojecthassponsororganisationcountry", + "safeprojectsponsorinformationprojecthassponsororganisationsector", + "safeprojectsponsorinformationprojecthassponsororganisationsize", + "safeprojectsponsorinformationprojecthassponsororganisationadditionaldetails", + "safeprojectsponsorinformationprojecthassponsorcontactfullname", + "safeprojectsponsorinformationprojecthassponsorcontactemail", + "safeprojectsponsorinformationprojecthassponsorcontacttelephone" + ], + "type": "buttonInput" + } + } + ], + "value": "Yes", + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } + ], + "type": "radioOptionsInput", + "label": "Does your project have a sponsor?" + }, + "validations": [ + { + "message": "Please select an option", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "lockedQuestion": 1, + "defaultQuestion": 1, + "questionId": "safeprojectsponsorinformationprojecthassponsor" + } + ] + }, + { + "questions": [ + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterest", + "defaultQuestion": 1, + "input": { + "type": "radioOptionsInput", + "label": "Is there a commercial interest in this project?", + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationname", + "question": "Organisation name", + "guidance": "Please confirm organisation name.", + "input": { + "type": "textInput" + } + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1", + "input": { + "type": "textInput" + }, + "question": "Registered address (line 1)", + "guidance": "Please confirm organisation address." + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", + "question": "Registered address (line 2)", + "input": { + "type": "textInput" + } + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", + "input": { + "type": "textInput" + }, + "guidance": "Please confirm organisation postcode.", + "question": "Postcode" + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcity", + "input": { + "type": "textInput" + }, + "question": "City", + "guidance": "Please confirm organisation city." + }, + { + "input": { + "type": "textInput" + }, + "guidance": "Please confirm organisation country.", + "question": "Country", + "questionId": "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry" + }, + { + "questionId": "safeprojectdeclarationofinteresthascommercialinterestinterestnature", + "input": { + "type": "textareaInput" + }, + "question": "Describe the nature of interest" + }, + { + "label": "Public interest", + "questionId": "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest", + "question": "Public interest", + "input": { + "options": [ + { + "value": "I confirm that any commercial interest is public interest related.", + "text": "I confirm that any commercial interest is public interest related." + } + ], + "type": "checkboxOptionsInput" + } + }, + { + "questionId": "add-safeproject-declarationofinterest", + "input": { + "class": "btn btn-primary addButton", + "text": "Add another organisation?", + "action": "addRepeatableQuestions", + "questionIds": [ + "safeprojectdeclarationofinteresthascommercialinterestorganisationname", + "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline1", + "safeprojectdeclarationofinteresthascommercialinterestorganisationaddressline2", + "safeprojectdeclarationofinteresthascommercialinterestorganisationpostcode", + "safeprojectdeclarationofinteresthascommercialinterestorganisationcity", + "safeprojectdeclarationofinteresthascommercialinterestorganisationcountry", + "safeprojectdeclarationofinteresthascommercialinterestinterestnature", + "safeprojectdeclarationofinteresthascommercialinterestconfirmpublicinterest" + ], + "type": "buttonInput", + "separatorText": "Organisation Details" + } + } + ] + }, + { + "value": "No", + "text": "No" + } + ], + "required": true + }, + "validations": [ + { + "message": "Please select an option", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "lockedQuestion": 1, + "guidance": "Please indicate if there is any commercial aspect or dimension to the project or its outcomes.", + "question": "Is there a commercial interest in this project?" + } + ], + "questionSetHeader": "Declaration of interest", + "questionSetId": "safeproject-declarationofinterest" + }, + { + "questions": [ + { + "guidance": "Intellectual Property is the tangible output of any intellectual activity that is new or previously undescribed. It has an owner; it can be bought, sold or licensed and must be adequately protected. It can include inventions, industrial processes, software, data, written work, designs and images. \nAny research which could potentially lead to intellectual property rights for you or your employer should be discussed with your employer and your R&D office as early as possible in the planning of the research.", + "question": "Please indicate if the research could lead to the development of a new product/process or the generation of intellectual property.", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "safeprojectintellectualpropertydevelopmentnewproductprocess" + } + ], + "questionSetHeader": "Intellectual property", + "questionSetId": "safeproject-intellectualproperty" + }, + { + "questions": [ + { + "lockedQuestion": 1, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "input": { + "required": true, + "type": "textareaInput" + }, + "question": "Please indicate the data necessary to conduct the study, the data fields required and the justifications for each field.", + "guidance": "Please list and make clear the details on the information from each dataset that you would like to have access to.\n\nYou can either upload a file including all data fields, variables and justification for each field/variable or use the free text box. Documents can be uploaded in the Additional Files section of this form.\n\nData requested can only be provided for processing data for specific purpose so you should ensure you explain why each dataset is required to meet your project objectives. Please ensure you have discussed your requirements with the data custodian.\n\nPlease justify why you need each requested dataset to ensure that the appropriateness, proportionality and risk associated can be carefully considered.\nIf you are requesting identifiable or potentially identifiable variables, please justify why you need each variable.\n\nPlease also include year/release of the datasets you propose to access.", + "questionId": "safedatadatafieldsdatarequiredjustification", + "defaultQuestion": 1 + }, + { + "input": { + "type": "checkboxOptionsInput", + "label": "Data fields indicated via file upload", + "options": [ + { + "text": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field.", + "value": "I confirm that I have enclosed a list of datasets, fields and variables required for the study as well as justification for each field." + } + ] + }, + "lockedQuestion": 0, + "guidance": "Documents can be uploaded in the Additional Files section of this form.", + "question": "Data fields indicated via file upload", + "questionId": "safedatadatafieldsfileuploaded", + "defaultQuestion": 1 + }, + { + "question": "Inclusion and exclusion criteria (including date parameters)", + "guidance": "A description of precisely the criteria which define the patients to be included and to be excluded from the data extract you are requesting should be provided. \n\nThis should include precise date parameters for the start and end of the range requested (dd/mm/yy) and explain which dated project field will be used to define the requested cohort (e.g. date of admission or date of operation).\n", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "safedatadatafieldsinclusionexclusioncriteria" + }, + { + "questionId": "safedatadatafieldsdatarefreshrequired", + "defaultQuestion": 1, + "input": { + "options": [ + { + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safedatadatafieldsdatarefreshrequiredperiodicity", + "question": "How often will the data refreshes be needed?", + "guidance": "Please indicate how often data refreshes will be needed. ", + "input": { + "type": "radioOptionsInput", + "label": "How often will the data refreshes be needed?", + "options": [ + { + "text": "Every month", + "value": "Every month" + }, + { + "value": "Every 3 months", + "text": "Every 3 months" + }, + { + "value": "Every 6 months", + "text": "Every 6 months" + }, + { + "text": "Every 12 months", + "value": "Every 12 months" + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ { - "value": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject.", - "text": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject." + "input": { + "type": "textInput" + }, + "question": "If other, please specify", + "questionId": "safedatadatafieldsdatarefreshrequiredperiodicityotherdetails" } - ] + ] + } + ] + } + } + ], + "text": "Yes" + }, + { + "text": "No", + "value": "No" + } + ], + "type": "radioOptionsInput", + "label": "Will you require periodic refreshes of the data?" + }, + "lockedQuestion": 0, + "question": "Will you require periodic refreshes of the data?", + "guidance": "Please indicate if data refreshers will be required." + }, + { + "questionId": "safedatadatafieldsgeographicalcoverage", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "type": "checkboxOptionsInput", + "label": "Geographical coverage of datasets requested", + "options": [ + { + "text": "England", + "value": "England" + }, + { + "value": "Wales", + "text": "Wales" + }, + { + "text": "Scotland", + "value": "Scotland" + }, + { + "value": "Northern Ireland", + "text": "Northern Ireland" + }, + { + "value": "Other", + "conditionalQuestions": [ + { + "question": "If other, please specify", + "input": { + "type": "textInput" }, - "question": "Article 9 conditions", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "question": "Article 9 legal basis justification", - "guidance": "Please provide justification for selected Article 9 conditions.", - "questionId": "safedatalawfulbasisgdprarticle9basisjustification", - "validations": [{ "message": "Please enter a value", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], - "input": { "type": "textareaInput", "required": true }, - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "questionId": "safedatadatafieldsgeographicalcoverageotherdetails" + } + ], + "text": "Other" + } + ] + }, + "guidance": "Please indicate geographical coverage of the datasets requested. ", + "question": "Geographical coverage of datasets requested" + }, + { + "defaultQuestion": 1, + "questionId": "safedatadatafieldsindividualidentifiers", + "guidance": "Please indicate whether you will be requiring data that contains individual identifiers.\n\nPersonal data means any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person. ", + "question": "Are you requesting data that contains individual identifiers?", + "lockedQuestion": 0, + "input": { + "options": [ + { + "text": "Not applicable", + "value": "Not applicable" + }, + { + "text": "Yes", + "value": "Yes" + }, + { + "value": "No", + "conditionalQuestions": [ + { + "questionId": "safedatadatafieldsindividualidentifiersanonymousdata", + "input": { + "options": [ + { + "value": "Not applicable", + "text": "Not applicable" + }, + { + "value": "Yes", + "text": "Yes" + }, + { + "text": "No", + "value": "No" + } + ], + "type": "radioOptionsInput", + "label": "Are you requesting anonymous data?" + }, + "question": "Are you requesting anonymous data?", + "guidance": "Please indicate whether you will be requiring anonymous data.\n\nAnonymised data is information which does not relate to an identified or identifiable natural person or to personal data rendered anonymous in such a manner that the data subject is not or no longer identifiable. \nIndividual patient level data may also very occasionally be categorised as anonymous. \nJust removing personal identifiers does not constitute anonymisation, as there may still be combinations of variables that might allow anyone to directly or indirectly identify individuals. True anonymisation is irreversible and hard to achieve.\n" + }, + { + "questionId": "safedatadatafieldsindividualidentifiersdeidentifieddata", + "question": "Are you requesting de-identified data?", + "guidance": "Please indicate whether you will be requiring de-identified data. \n\nNote that pseudonymised data does not change the status of the data as personal data. Personal data which have undergone pseudonymisation, which could be attributed to a natural person by the use of additional information should be considered to be information on an identifiable natural person.\n\nIf any pseudonymous data or data being described by the applicant as 'anonymous' will be shared with third parties, the fields to be shared should be clarified.\n", + "input": { + "options": [ + { + "value": "Not applicable", + "text": "Not applicable" + }, + { + "text": "Yes", + "value": "Yes" + }, + { + "text": "No", + "value": "No" + } + ], + "type": "radioOptionsInput", + "label": "Are you requesting de-identified data?" + } + } + ], + "text": "No" + } ], - "questionSetHeader": "Lawful basis", - "questionSetId": "safedata-lawfulbasis" - }, - { - "questionSetHeader": "Confidentiality avenue", - "questionSetId": "safedata-confidentialityavenue", - "questions": [ - { - "validations": [{ "message": "Please select an option", "type": "isLength", "params": [{ "$numberDouble": "1.0" }] }], - "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformation", - "input": { - "type": "radioOptionsInput", - "required": true, - "label": "Please provide the legal basis to process confidential information", - "options": [ - { "text": "Not applicable", "value": "Not applicable" }, - { - "conditionalQuestions": [ - { - "input": { - "type": "checkboxOptionsInput", - "options": [ - { - "value": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested", - "text": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested" - } - ] - }, - "questionId": "safedataconfidentialityavenueinformedconsentevidence", - "label": "Informed consent evidence", - "question": "Informed consent evidence" - } - ], - "text": "Informed consent", - "value": "Informed consent" - }, - { - "value": "Section 251 support", - "text": "Section 251 support", - "conditionalQuestions": [ - { - "input": { - "type": "checkboxOptionsInput", - "options": [ - { - "text": "I have enclosed a copy of the S251 approved amendments and any renewal letters", - "value": "I have enclosed a copy of the S251 approved amendments and any renewal letters" - } - ] - }, - "questionId": "safedataconfidentialityavenuesection251exemptionevidence", - "label": "Section 251 exemption evidence", - "question": "Section 251 exemption evidence" - }, - { - "question": "CAG reference", - "questionId": "safedataconfidentialityavenuesection251cagreference", - "input": { "type": "textInput" } - }, - { - "question": "The section 251 approval enables the applicant to", - "questionId": "safedataconfidentialityavenuesection251approvaldetails", - "input": { - "options": [ - { "text": "Hold/receive personal data", "value": "Hold/receive personal data" }, - { "text": "Transfer/access personal data", "value": "Transfer/access personal data" }, - { "value": "Operate on and link personal data", "text": "Operate on and link personal data" }, - { - "conditionalQuestions": [ - { - "question": "If other, please specify", - "input": { "type": "textInput" }, - "questionId": "safedataconfidentialityavenuesection251approvaldetailsotherdetails" - } - ], - "value": "Other", - "text": "Other" - } - ], - "type": "checkboxOptionsInput" - }, - "label": "The section 251 approval enables the applicant to" - } - ] - }, + "label": "Are you requesting data that contains individual identifiers?", + "type": "radioOptionsInput" + } + }, + { + "questionId": "safedatadatafieldsaggregatedorrecordlevel", + "defaultQuestion": 1, + "input": { + "type": "radioOptionsInput", + "label": "Do you require aggregated or record level data?", + "options": [ + { + "value": "Yes", + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "lockedQuestion": 0, + "question": "Do you require aggregated or record level data?", + "guidance": "Record level data typically relates to a single individual. There may be one or many records per individual. Such data would usually carry a risk of re-identification, and use of such data would be subject to strict controls.\n\nAggregate data would typically be 'counts' of an event - for example how many people had a particular operation over a specific time period. Aggregate data is not always anonymous data, and therefore may also be subject to specific controls.\n" + } + ], + "questionSetId": "safedata-datafields", + "questionSetHeader": "Data fields" + }, + { + "questions": [ + { + "defaultQuestion": 1, + "questionId": "safedatadatafieldsdatacustodianconductanalysis", + "guidance": "Please indicate if you wish to commission the data custodian to conduct the analysis for you, minimising your exposure to the data.", + "question": "Do you wish to commission the data custodian to conduct the analysis for you, minimising your exposure to the data?", + "input": { + "label": "Do you wish to commission the data custodian to conduct the analysis for you, minimising your exposure to the data?", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "lockedQuestion": 0 + } + ], + "questionSetId": "safedata-analysis", + "questionSetHeader": "Analysis" + }, + { + "questionSetHeader": "Samples required", + "questionSetId": "safedata-samplesrequired", + "questions": [ + { + "question": "Do you wish to request access to any additional resources (samples or tissues)?", + "input": { + "type": "radioOptionsInput", + "label": "Do you wish to request access to any additional resources (samples or tissues)?", + "options": [ + { + "value": "Yes", + "text": "Yes", + "conditionalQuestions": [ + { + "question": "Specify all datasets, organisations which will perform the linkage and how the linkage will take place.", + "guidance": "Please specify your requirements for the additional resources you are requesting.\n\nSamples from healthy or diseased donors? If diseased, please specify which disease.\n\nWhat material types do you require?\ne.g. whole blood, plasma, tissue, etc.\n\nWhat do you intend to do with the sample?\ne.g. cell culture, genetic analysis, histology etc.\n(This will inform which storage conditions you require.)\n\nHow many samples do you need over what timeframe?\n\nHow many samples can you receive at one time?\n\nWhat sized sample or volume do you require?\n\nWhat are your exclusion criteria?\ne.g. no HIV, no cancer, above 18, male, female\n\nWhat associated clinical information do you need?", + "input": { + "type": "textareaInput" + }, + "questionId": "safedatadatafieldsadditionalresourcesdetails" + } + ] + }, + { + "text": "No", + "value": "No" + } + ] + }, + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "safedatadatafieldsadditionalresources", + "guidance": "" + } + ] + }, + { + "questionSetId": "safedata-otherdatasetsintentiontolinkdata", + "questionSetHeader": "Other datasets - Intention to link data", + "questions": [ + { + "input": { + "type": "radioOptionsInput", + "label": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", + "required": true, + "options": [ + { + "text": "Yes", + "conditionalQuestions": [ + { + "question": "Specify all datasets, organisations which will perform the linkage and how the linkage will take place.", + "guidance": "Please include details of the organisations undertaking the process of linkage.", + "input": { + "type": "textareaInput" + }, + "questionId": "safedataotherdatasetslinkadditionaldatasetslinkagedetails" + } + ], + "value": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "validations": [ + { + "message": "Please select an option", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "lockedQuestion": 1, + "question": "Do you intend for the datasets requested to be linked with any additional datasets, other than the datasets listed in this application?", + "guidance": "Please specify if you intend for the datasets to be linked with any additional datasets. Please also provide relevant information on the organisations undertaking linkages and provide a data flow diagram where applicable. Documents can be uploaded in the Additional Files section of this form.", + "questionId": "safedataotherdatasetslinkadditionaldatasets", + "defaultQuestion": 1 + }, + { + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 1, + "question": "Please summarise the risks/mitigations considered.", + "questionId": "safedataotherdatasetsrisksmitigations", + "defaultQuestion": 1, + "guidance": "" + } + ] + }, + { + "questions": [ + { + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "guidance": "Please specify what type of statistical analyses you are planning.", + "question": "What forms of statistical analysis are planned?", + "questionId": "safedatastatisticalanalysisformsplanned", + "defaultQuestion": 0 + }, + { + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "guidance": "Please explain how you intend to present this in the final output.", + "question": "How is it intended that this will be presented in the final output?", + "questionId": "safedatastatisticalanalysisfinaloutput", + "defaultQuestion": 0 + }, + { + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "guidance": "For example, an age range of 90-100 in a rare disease may result in a cell value of 1.", + "question": "What is the smallest cell value that is likely to be generated by this analysis and how will this be managed to avoid disclosure?", + "questionId": "safedatastatisticalanalysissmallestcellvalue", + "defaultQuestion": 0 + }, + { + "questionId": "safedatastatisticalanalysisconfounding", + "defaultQuestion": 0, + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "What are your plans for addressing confounding?", + "guidance": "" + }, + { + "questionId": "safedatastatisticalanalysismissingdata", + "defaultQuestion": 0, + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "What are your plans for addressing missing data?", + "guidance": "" + }, + { + "question": "Please present your feasibility calculation", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "defaultQuestion": 0, + "questionId": "safedatastatisticalanalysisfeasibility", + "guidance": "" + }, + { + "questionId": "safedatastatisticalanalysisstatisticalpower", + "defaultQuestion": 0, + "lockedQuestion": 0, + "input": { + "label": "Has the size of the study been informed by a formal statistical power calculation?", + "type": "radioOptionsInput", + "options": [ + { + "text": "Yes", + "value": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "question": "Has the size of the study been informed by a formal statistical power calculation?", + "guidance": "" + }, + { + "questionId": "safedatastatisticalanalysisstatistician", + "defaultQuestion": 0, + "input": { + "label": "Has a statistician given an opinion about the statistical aspects of the research?", + "type": "radioOptionsInput", + "options": [ + { + "value": "Yes", + "conditionalQuestions": [ + { + "questionId": "safedatastatisticalanalysisstatisticianyes", + "input": { + "type": "textareaInput" + }, + "question": "If yes, please give a brief summary of advice offered", + "guidance": "If available, please attach a copy of the comments as an additional file. Documents can be uploaded in the Additional Files section of this form." + } + ], + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "lockedQuestion": 0, + "question": "Has a statistician given an opinion about the statistical aspects of the research?", + "guidance": "" + }, + { + "questionId": "safedatastatisticalanalysisalgorithmtraining", + "defaultQuestion": 0, + "input": { + "options": [ + { + "value": "Yes", + "text": "Yes" + }, + { + "text": "No", + "value": "No" + } + ], + "label": "Will the data requested be used for algorithm generation and training?", + "type": "radioOptionsInput" + }, + "lockedQuestion": 0, + "question": "Will the data requested be used for algorithm generation and training?", + "guidance": "These questions are to help us understand the different uses to which the data is being put to." + }, + { + "guidance": "These questions are to help us understand the different uses to which the data is being put to.", + "question": "Will the data requested be used for internal validation?", + "lockedQuestion": 0, + "input": { + "type": "radioOptionsInput", + "label": "Will the data requested be used for internal validation?", + "options": [ + { + "text": "Yes", + "value": "Yes" + }, + { + "text": "No", + "value": "No" + } + ] + }, + "defaultQuestion": 0, + "questionId": "safedatastatisticalanalysisinternalvalidation" + }, + { + "guidance": "These questions are to help us understand the different uses to which the data is being put to.", + "question": "Will the data requested be used for external validation?", + "lockedQuestion": 0, + "input": { + "type": "radioOptionsInput", + "label": "Will the data requested be used for external validation?", + "options": [ + { + "text": "Yes", + "value": "Yes" + }, + { + "text": "No", + "value": "No" + }, + { + "conditionalQuestions": [ + { + "questionId": "safedatastatisticalanalysisexternalvalidationotherdetails", + "question": "If other, please specify", + "input": { + "type": "textareaInput" + } + } + ], + "value": "Other", + "text": "Other" + } + ] + }, + "defaultQuestion": 0, + "questionId": "safedatastatisticalanalysisexternalvalidation" + }, + { + "input": { + "type": "radioOptionsInput", + "label": "Will the data be subject to any machine learning (ML) techniques?", + "options": [ + { + "value": "Yes", + "conditionalQuestions": [ + { + "input": { + "type": "textareaInput" + }, + "question": "If yes, please specify the type of machine learning technique", + "questionId": "safedatastatisticalanalysismachinelearningtechniquesdetails" + } + ], + "text": "Yes" + }, + { + "value": "No", + "text": "No" + } + ] + }, + "lockedQuestion": 0, + "question": "Will the data be subject to any machine learning (ML) techniques?", + "guidance": "These questions are to help us understand the different uses to which the data is being put to.", + "questionId": "safedatastatisticalanalysismachinelearningtechniques", + "defaultQuestion": 0 + } + ], + "questionSetHeader": "Statistical analysis", + "questionSetId": "safedata-statisticalanalysis" + }, + { + "questionSetHeader": "Lawful basis", + "questionSetId": "safedata-lawfulbasis", + "questions": [ + { + "questionId": "safedatalawfulbasisgdprarticle6basis", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "options": [ + { + "text": "Not applicable", + "value": "Not applicable" + }, + { + "value": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;", + "text": "(a) the data subject has given consent to the processing of his or her personal data for one or more specific purposes;" + }, + { + "text": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;", + "value": "(b) processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract;" + }, + { + "value": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;", + "text": "(c) processing is necessary for compliance with a legal obligation to which the controller is subject;" + }, + { + "text": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;", + "value": "(d) processing is necessary in order to protect the vital interests of the data subject or of another natural person;" + }, + { + "value": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;", + "text": "(e) processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller;" + }, + { + "value": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child.", + "text": "(f) processing is necessary for the purposes of the legitimate interests pursued by the controller or by a third party, except where such interests are overridden by the interests or fundamental rights and freedoms of the data subject which require protection of personal data, in particular where the data subject is a child." + } + ], + "label": "Article 6 lawful basis", + "type": "radioOptionsInput" + }, + "guidance": "The lawful bases for processing are set out in Article 6 of the GDPR (https://gdpr-info.eu/art-6-gdpr/). At least one legal basis must apply whenever you process personal data. Please select the appropriate Article 6 lawful basis. Processing shall be lawful only if, and to the extent, that at least one of the following applies.", + "question": "Article 6 lawful basis" + }, + { + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "guidance": "Please provide justification for selected Article 6 lawful basis.", + "question": "Article 6 legal basis justification", + "questionId": "safedatalawfulbasisgdprarticle6basisjustification", + "defaultQuestion": 1 + }, + { + "questionId": "safedatalawfulbasisgdprarticle9conditions", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "label": "Article 9 conditions", + "type": "radioOptionsInput", + "options": [ + { + "value": "Not applicable", + "text": "Not applicable" + }, + { + "text": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;", + "value": "(a) the data subject has given explicit consent to the processing of those personal data for one or more specified purposes, except where Union or Member State law provide that the prohibition referred to in paragraph 1 may not be lifted by the data subject;" + }, + { + "text": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;", + "value": "(b) processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law in so far as it is authorised by Union or Member State law or a collective agreement pursuant to Member State law providing for appropriate safeguards for the fundamental rights and the interests of the data subject;" + }, + { + "text": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;", + "value": "(c) processing is necessary to protect the vital interests of the data subject or of another natural person where the data subject is physically or legally incapable of giving consent;" + }, + { + "value": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;", + "text": "(d) processing is carried out in the course of its legitimate activities with appropriate safeguards by a foundation, association or any other not-for-profit body with a political, philosophical, religious or trade union aim and on condition that the processing relates solely to the members or to former members of the body or to persons who have regular contact with it in connection with its purposes and that the personal data are not disclosed outside that body without the consent of the data subjects;" + }, + { + "value": "(e) processing relates to personal data which are manifestly made public by the data subject;", + "text": "(e) processing relates to personal data which are manifestly made public by the data subject;" + }, + { + "text": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;", + "value": "(f) processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity;" + }, + { + "text": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;", + "value": "(g) processing is necessary for reasons of substantial public interest, on the basis of Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject;" + }, + { + "text": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;", + "value": "(h) processing is necessary for the purposes of preventive or occupational medicine, for the assessment of the working capacity of the employee, medical diagnosis, the provision of health or social care or treatment or the management of health or social care systems and services on the basis of Union or Member State law or pursuant to contract with a health professional and subject to the conditions and safeguards referred to in paragraph 3;" + }, + { + "text": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;", + "value": "(i) processing is necessary for reasons of public interest in the area of public health, such as protecting against serious cross-border threats to health or ensuring high standards of quality and safety of health care and of medicinal products or medical devices, on the basis of Union or Member State law which provides for suitable and specific measures to safeguard the rights and freedoms of the data subject, in particular professional secrecy;" + }, + { + "value": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject.", + "text": "(j) processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes in accordance with Article 89(1) based on Union or Member State law which shall be proportionate to the aim pursued, respect the essence of the right to data protection and provide for suitable and specific measures to safeguard the fundamental rights and the interests of the data subject." + } + ] + }, + "guidance": "Please select appropriate Article 9 conditions. \n \nProcessing of personal data revealing racial or ethnic origin, political opinions, religious or philosophical beliefs, or trade union membership, and the processing of genetic data, biometric data for the purpose of uniquely identifying a natural person, data concerning health or data concerning a natural person's sex life or sexual orientation shall be prohibited. This does not apply if one of the following applies.\n", + "question": "Article 9 conditions" + }, + { + "defaultQuestion": 1, + "questionId": "safedatalawfulbasisgdprarticle9basisjustification", + "question": "Article 9 legal basis justification", + "guidance": "Please provide justification for selected Article 9 conditions.", + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0 + } + ] + }, + { + "questionSetId": "safedata-confidentialityavenue", + "questionSetHeader": "Confidentiality avenue", + "questions": [ + { + "input": { + "type": "radioOptionsInput", + "label": "Please provide the legal basis to process confidential information", + "options": [ + { + "value": "Not applicable", + "text": "Not applicable" + }, + { + "value": "Informed consent", + "conditionalQuestions": [ + { + "question": "Informed consent evidence", + "guidance": "Please ensure a copy of the consent form(s) and patient information sheet have been provided. Documents can be uploaded in the Additional Files section of this form.", + "input": { + "options": [ + { + "value": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested", + "text": "I have enclosed a blank copy of the patient consent form(s) and all related information sheets relevant to the time period in the data requested" + } + ], + "type": "checkboxOptionsInput" + }, + "questionId": "safedataconfidentialityavenueinformedconsentevidence", + "label": "Informed consent evidence" + } + ], + "text": "Informed consent" + }, + { + "text": "Section 251 support", + "conditionalQuestions": [ + { + "label": "Section 251 exemption evidence", + "questionId": "safedataconfidentialityavenuesection251exemptionevidence", + "question": "Section 251 exemption evidence", + "guidance": "Please ensure a copy of the Section 251 exemption has been provided. Documents can be uploaded in the Additional Files section of this form.", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I have enclosed a copy of the S251 approved amendments and any renewal letters", + "text": "I have enclosed a copy of the S251 approved amendments and any renewal letters" + } + ] + } + }, + { + "questionId": "safedataconfidentialityavenuesection251cagreference", + "question": "CAG reference", + "input": { + "type": "textInput" + } + }, + { + "guidance": "Please indicate what the Section 251 exemption permits you to do as part of your project.", + "question": "The section 251 approval enables the applicant to", + "input": { + "options": [ + { + "value": "Hold/receive personal data", + "text": "Hold/receive personal data" + }, + { + "text": "Transfer/access personal data", + "value": "Transfer/access personal data" + }, + { + "text": "Operate on and link personal data", + "value": "Operate on and link personal data" + }, + { + "value": "Other", + "conditionalQuestions": [ { - "text": "Other", - "value": "Other", - "conditionalQuestions": [ - { - "question": "If other, please specify", - "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformationotherdetails", - "input": { "type": "textInput" } - } - ] + "question": "If other, please specify", + "input": { + "type": "textInput" + }, + "questionId": "safedataconfidentialityavenuesection251approvaldetailsotherdetails" } - ] + ], + "text": "Other" + } + ], + "type": "checkboxOptionsInput" }, - "question": "Please provide the legal basis to process confidential information", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "label": "The section 251 approval enables the applicant to", + "questionId": "safedataconfidentialityavenuesection251approvaldetails" + } + ], + "value": "Section 251 support" + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ + { + "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformationotherdetails", + "input": { + "type": "textInput" + }, + "question": "If other, please specify" + } + ] + } ] + }, + "lockedQuestion": 1, + "question": "Please provide the legal basis to process confidential information", + "guidance": "Please confirm if consent is in place or underway for all disclosures of confidential information, if you have Section 251 exemption, or any other legal basis that you require for the project.\n\nFor England and Wales, please specify if Section 251 exemption is currently being sought and if so, please provide a Confidentiality Advisory group reference code.\n\nIn Scotland applications are required for the consented and unconsented use of data.\n", + "questionId": "safedataconfidentialityavenuelegalbasisconfidentialinformation", + "defaultQuestion": 1 + } + ] + }, + { + "questionSetHeader": "Ethical approval", + "questions": [ + { + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes" + }, + { + "text": "No", + "value": "No" + } + ], + "label": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?", + "required": true, + "type": "radioOptionsInput", + "guidance": "" + }, + "defaultQuestion": 1, + "questionId": "safedataethicsapprovalinsightresearch", + "lockedQuestion": 1, + "question": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?", + "validations": [ + { + "params": [ + 1 + ], + "message": "Please select an option", + "type": "isLength" + } + ], + "guidance": "" }, { - "questions": [ - { - "question": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?", + "input": { + "options": [ + { + "text": "Yes", + "value": "Yes", + "conditionalQuestions": [ + { + "question": "Approval - REC committee name", + "questionId": "safedataethicsapprovalapprovalobtainedreccommitteename", + "guidance": "Please provide REC or other committee details.", "input": { - "options": [ - { "value": "Yes", "text": "Yes" }, - { "value": "No", "text": "No" } - ], - "type": "radioOptionsInput", - "label": "Do you seek for your project to be approved under the generic favourable ethical opinion of the INSIGHT Research Database (Ref: 20/WS/0087)?" + "type": "textInput" + } + }, + { + "input": { + "type": "textInput" + }, + "questionId": "safedataethicsapprovalapprovalobtainedrecreferencenumber", + "question": "Approval - REC reference number" + }, + { + "question": "Approval - Other committee", + "questionId": "safedataethicsapprovalapprovalobtainedothercommittee", + "input": { + "type": "textInput" + } + }, + { + "input": { + "options": [ + { + "text": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments", + "value": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments" + } + ], + "type": "checkboxOptionsInput" + }, + "guidance": "Please confirm a copy of the REC referenced above has been enclosed. Documents can be uploaded in the Additional Files section of this form.", + "label": "Evidence of REC approval", + "questionId": "safedataethicsapprovalapprovalobtainedevidenceenclosed", + "question": "Evidence of REC approval" + } + ] + }, + { + "text": "No", + "value": "No", + "conditionalQuestions": [ + { + "input": { + "type": "textInput" + }, + "guidance": "If not, please provide details.", + "questionId": "safedataethicsapprovalapprovalobtainednotobtaineddetails", + "question": "If not, please provide more details" + } + ] + }, + { + "value": "Approval pending", + "text": "Approval pending", + "conditionalQuestions": [ + { + "input": { + "type": "textInput" }, - "questionId": "safedataethicsapprovalinsightresearch", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "validations": [{ "type": "isLength", "message": "Please select an option", "params": [{ "$numberDouble": "1.0" }] }], - "questionId": "safedataethicsapprovalapprovalobtained", - "input": { - "options": [ + "guidance": "If approval is pending, please provide details.", + "questionId": "safedataethicsapprovalapprovalobtainedpendingdetails", + "question": "If approval is pending, please provide more details" + } + ] + }, + { + "text": "Not required", + "value": "Not required", + "conditionalQuestions": [ + { + "input": { + "type": "textInput" + }, + "guidance": "If not, please provide details.", + "questionId": "safedataethicsapprovalapprovalobtainednotrequireddetails", + "question": "If not, please provide more details" + } + ] + } + ], + "label": "Has ethical approval been obtained?", + "required": true, + "type": "radioOptionsInput" + }, + "defaultQuestion": 1, + "questionId": "safedataethicsapprovalapprovalobtained", + "guidance": "Please confirm if research ethics approval has been obtained. Request for research purposes must include enclose evidence of ethics approval or evidence that this is not required.", + "lockedQuestion": 1, + "question": "Has ethical approval been obtained?", + "validations": [ + { + "params": [ + 1 + ], + "message": "Please select an option", + "type": "isLength" + } + ] + } + ], + "questionSetId": "safedata-ethicalapproval" + }, + { + "questionSetHeader": "Storage and processing", + "questionSetId": "safesettings-storageandprocessing", + "questions": [ + { + "questionId": "safedatastorageandprocessingaccessmethod", + "defaultQuestion": 1, + "input": { + "options": [ + { + "text": "Via a Trusted Research Environment", + "value": "Via a Trusted Research Environment", + "conditionalQuestions": [ + { + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironment", + "question": "In which Trusted Research Environment will the data be accessed?", + "input": { + "type": "radioOptionsInput", + "label": "In which Trusted Research Environment will the data be accessed?", + "options": [ + { + "text": "Secure e-Research Platform (SeRP)", + "value": "Secure e-Research Platform (SeRP)" + }, + { + "text": "NI Honest Broker Service (NI HBS)", + "value": "NI Honest Broker Service (NI HBS)" + }, + { + "value": "Scottish National Safe Haven (SNSH)", + "text": "Scottish National Safe Haven (SNSH)" + }, + { + "text": "NHS Digital", + "conditionalQuestions": [ { - "conditionalQuestions": [ - { - "guidance": "Please provide REC or other committee details.", - "input": { "type": "textInput" }, - "questionId": "safedataethicsapprovalapprovalobtainedreccommitteename", - "question": "Approval - REC committee name" - }, - { - "questionId": "safedataethicsapprovalapprovalobtainedrecreferencenumber", - "input": { "type": "textInput" }, - "question": "Approval - REC reference number" - }, - { - "question": "Approval - Other committee", - "questionId": "safedataethicsapprovalapprovalobtainedothercommittee", - "input": { "type": "textInput" } - }, - { - "label": "Evidence of REC approval", - "guidance": "Please confirm a copy of the REC referenced above has been enclosed.", - "input": { - "options": [ - { - "text": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments", - "value": "I have enclosed a copy of the final REC approval letter and letters documenting any REC approved amendments" - } - ], - "type": "checkboxOptionsInput" - }, - "questionId": "safedataethicsapprovalapprovalobtainedevidenceenclosed", - "question": "Evidence of REC approval" - } - ], - "value": "Yes", - "text": "Yes" - }, + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentdspdetails", + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "input": { + "type": "textInput", + "required": true + }, + "question": "Does the applicant organisation have a DSP Toolkit? If so, please provide details including code, score and version completed." + } + ], + "value": "NHS Digital" + }, + { + "text": "SAIL Databank", + "value": "SAIL Databank" + }, + { + "text": "ONS Secure Research Service (SRS)", + "value": "ONS Secure Research Service (SRS)" + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ { - "text": "No", - "value": "No", - "conditionalQuestions": [ - { - "questionId": "safedataethicsapprovalapprovalobtainednotobtaineddetails", - "input": { "type": "textInput" }, - "question": "If not, please provide more details" - } - ] + "question": "If other, please specify", + "input": { + "type": "textInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentotherdetails" + } + ] + } + ] + } + } + ] + }, + { + "conditionalQuestions": [ + { + "input": { + "type": "textInput" + }, + "question": "Registered name of organisation", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationname" + }, + { + "question": "Registered number", + "input": { + "type": "textInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber" + }, + { + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "Storage", + "text": "Storage" + }, + { + "text": "Processing", + "value": "Processing" + } + ] + }, + "question": "Will this organisation be storing or processing the data?", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", + "label": "Will this organisation be storing or processing the data?" + }, + { + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance", + "label": "What type of security assurance does this organisation have in place?", + "input": { + "options": [ + { + "text": "Data security and Protection Toolkit (DSP Toolkit)", + "value": "Data security and Protection Toolkit (DSP Toolkit)", + "conditionalQuestions": [ + { + "question": "DSP Toolkit organisation code", + "guidance": "As a data controller, the applicant's organisation should be registered with the Information Commissioner's Office (ICO). Please provide Security and Protection Toolkit (DSP Toolkit) details.", + "input": { + "type": "textInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsporganisationcode" }, { - "text": "Approval pending", - "value": "Approval pending", - "conditionalQuestions": [ - { - "question": "If approval is pending, please provide more details", - "input": { "type": "textInput" }, - "questionId": "safedataethicsapprovalapprovalobtainedpendingdetails", - "guidance": "If approval is pending, please provide details." - } - ] + "question": "DSP Toolkit score", + "input": { + "type": "textInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsptoolkitscore" }, { - "value": "Not required", - "text": "Not required", - "conditionalQuestions": [ - { - "question": "If not required, please provide details", - "guidance": "If ethics approval is not required, please explain why this is the case.", - "questionId": "safedataethicsapprovalapprovalobtainednotrequireddetails", - "input": { "type": "textInput" } - } - ] + "question": "DSP Toolkit version completed", + "input": { + "type": "textInput" + }, + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedspversioncompleted" } - ], - "label": "Has ethical approval been obtained?", - "type": "radioOptionsInput", - "required": true - }, - "guidance": "Please confirm if ethics approval has been obtained. Request for research purposes must include evidence of ethics approval or evidence that this is not required.", - "question": "Has ethical approval been obtained?", - "lockedQuestion": 1, - "defaultQuestion": 0 - } - ], - "questionSetHeader": "Ethical approval", - "questionSetId": "safedata-ethicalapproval" - }, - { - "questions": [ - { - "guidance": "Please specify if the data will be accessed within a Trusted Research Environment. ", - "input": { - "options": [ + ] + }, + { + "text": "ISO 27001", + "value": "ISO 27001", + "conditionalQuestions": [ { - "conditionalQuestions": [ - { - "guidance": "If you have selected 'Other', please specify the Trusted Research Environment.", - "input": { - "type": "radioOptionsInput", - "label": "In which Trusted Research Environment will the data be accessed?", - "options": [ - { "value": "Secure e-Research Platform (SeRP)", "text": "Secure e-Research Platform (SeRP)" }, - { "value": "NI Honest Broker Service (NI HBS)", "text": "NI Honest Broker Service (NI HBS)" }, - { "text": "Scottish National Safe Haven (SNSH)", "value": "Scottish National Safe Haven (SNSH)" }, - { - "text": "NHS Digital", - "value": "NHS Digital", - "conditionalQuestions": [ - { - "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentdspdetails", - "input": { "type": "textInput" }, - "question": "Does the applicant organisation have a DSP Toolkit? If so, please provide details including code, score and version completed." - } - ] - }, - { "text": "SAIL Databank", "value": "SAIL Databank" }, - { "value": "ONS Secure Research Service (SRS)", "text": "ONS Secure Research Service (SRS)" }, - { - "value": "Other", - "text": "Other", - "conditionalQuestions": [ - { - "question": "If other, please specify", - "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironmentotherdetails", - "input": { "type": "textInput" } - } - ] - } - ] - }, - "questionId": "safedatastorageandprocessingaccessmethodtrustedresearchenvironment", - "question": "In which Trusted Research Environment will the data be accessed?" - } + "guidance": "Please confirm that you have enclosed a copy of your ISO 27001 certificate. Documents can be uploaded in the Additional Files section of this form.", + "question": "Evidence of ISO 27001", + "input": { + "options": [ + { + "value": "I have enclosed a copy of my certificate", + "text": "I have enclosed a copy of my certificate" + } ], - "text": "Via a Trusted Research Environment", - "value": "Via a Trusted Research Environment" - }, + "type": "checkboxOptionsInput" + }, + "label": "Evidence of ISO 27001", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceiso27001evidence" + } + ] + }, + { + "conditionalQuestions": [ { - "conditionalQuestions": [ - { - "question": "Registered name of organisation", - "input": { "type": "textInput" }, - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationname", - "guidance": "Please provide ICO registration details." - }, - { - "question": "Registered number", - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber", - "input": { "type": "textInput" } - }, - { - "question": "Will this organisation be storing or processing the data?", - "label": "Will this organisation be storing or processing the data?", - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", - "input": { - "options": [ - { "value": "Storage", "text": "Storage" }, - { "text": "Processing", "value": "Processing" } - ], - "type": "checkboxOptionsInput" - } - }, - { - "label": "What type of security assurance does this organisation have in place?", - "guidance": "Adequate security assurance must be provided for all processing locations. Each organisation processing data that is not fully anonymous as part of this project must demonstrate that they have appropriate security arrangements are in place. Please confirm whether the applicant organisation has a compliant Data Security and Protection Toolkit.", - "input": { - "type": "checkboxOptionsInput", - "options": [ - { - "text": "Data security and Protection Toolkit (DSP Toolkit)", - "value": "Data security and Protection Toolkit (DSP Toolkit)", - "conditionalQuestions": [ - { - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsporganisationcode", - "input": { "type": "textInput" }, - "guidance": "As a data controller, the applicant's organisation should be registered with the Information Commissioner's Office (ICO). Please provide Security and Protection Toolkit (DSP Toolkit) details.", - "question": "DSP Toolkit organisation code" - }, - { - "question": "DSP Toolkit score", - "input": { "type": "textInput" }, - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedsptoolkitscore" - }, - { - "question": "DSP Toolkit version completed", - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurancedspversioncompleted", - "input": { "type": "textInput" } - } - ] - }, - { - "text": "ISO 27001", - "value": "ISO 27001", - "conditionalQuestions": [ - { - "label": "Evidence of ISO 27001", - "input": { - "options": [ - { - "text": "I have enclosed a copy of my certificate", - "value": "I have enclosed a copy of my certificate" - } - ], - "type": "checkboxOptionsInput" - }, - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceiso27001evidence", - "question": "Evidence of ISO 27001" - } - ] - }, - { - "conditionalQuestions": [ - { - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceslspevidence", - "input": { - "type": "checkboxOptionsInput", - "options": [ - { - "value": "I have enclosed a completed system level security policy for ODR review", - "text": "I have enclosed a completed system level security policy for ODR review" - } - ] - }, - "label": "Evidence of SLSP", - "question": "Evidence of SLSP" - } - ], - "text": "SLSP", - "value": "SLSP" - }, - { - "text": "Other", - "value": "Other", - "conditionalQuestions": [ - { - "question": "If other, please specify", - "guidance": "If you have selected 'Other', please specify the type of security assurance the organisation has put in place.", - "input": { "type": "textInput" }, - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceotherdetails" - } - ] - } - ] - }, - "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance", - "question": "What type of security assurance does this organisation have in place?" - }, - { - "questionId": "add-safesettings-storageandprocessing", - "input": { - "separatorText": "Organisation Details", - "type": "buttonInput", - "class": "btn btn-primary addButton", - "action": "addRepeatableQuestions", - "questionIds": [ - "safedatastorageandprocessingaccessmethodphysicallocationorganisationname", - "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber", - "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", - "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance" - ], - "text": "Add another organisation?" - } - } - ], - "value": "Via transfer to a physical location (Provide details of the processing/storage organisations below)", - "text": "Via transfer to a physical location (Provide details of the processing/storage organisations below)" + "label": "Evidence of SLSP", + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceslspevidence", + "input": { + "type": "checkboxOptionsInput", + "options": [ + { + "value": "I have enclosed a completed system level security policy for ODR review", + "text": "I have enclosed a completed system level security policy for ODR review" + } + ] + }, + "question": "Evidence of SLSP" } - ], - "label": "How will the data be accessed?", - "type": "checkboxOptionsInput" + ], + "value": "SLSP", + "text": "SLSP" + }, + { + "text": "Other", + "value": "Other", + "conditionalQuestions": [ + { + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassuranceotherdetails", + "question": "If other, please specify", + "input": { + "type": "textInput" + } + } + ], + "guidance": "If you have selected 'Other', please specify the type of security assurance the organisation has put in place." + } + ], + "type": "checkboxOptionsInput" }, - "questionId": "safedatastorageandprocessingaccessmethod", - "question": "How will the data be accessed?", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "guidance": "Adequate security assurance must be provided for all processing locations. Each organisation processing data that is not fully anonymous as part of this project must demonstrate that they have appropriate security arrangements are in place. Please confirm whether the applicant organisation has a compliant Data Security and Protection Toolkit.", + "question": "What type of security assurance does this organisation have in place?" + }, + { + "questionId": "safedatastorageandprocessingaccessmethodphysicallocationreasonfornotchoosingtre", + "question": "If you do not wish to access via a Trusted Research Environment, please provide the reason.", + "input": { + "type": "textareaInput" + } + }, + { + "questionId": "add-safesettings-storageandprocessing", + "input": { + "type": "buttonInput", + "questionIds": [ + "safedatastorageandprocessingaccessmethodphysicallocationorganisationname", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationregisterednumber", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationstoringorprocessing", + "safedatastorageandprocessingaccessmethodphysicallocationorganisationsecurityassurance", + "safedatastorageandprocessingaccessmethodphysicallocationreasonfornotchoosingtre" + ], + "separatorText": "Organisation Details", + "text": "Add another organisation?", + "action": "addRepeatableQuestions", + "class": "btn btn-primary addButton" + } + } + ], + "value": "Via transfer to a physical location (Provide details of the processing/storage organisations below)", + "text": "Via transfer to a physical location (Provide details of the processing/storage organisations below)" + } ], - "questionSetId": "safesettings-storageandprocessing", - "questionSetHeader": "Storage and processing" - }, + "type": "checkboxOptionsInput", + "label": "How will the data be accessed?" + }, + "lockedQuestion": 1, + "question": "How will the data be accessed?", + "guidance": "Please specify if the data will be accessed within a Trusted Research Environment. " + } + ] + }, + { + "questionSetId": "safesettings-dataflow", + "questionSetHeader": "Dataflow", + "questions": [ { - "questions": [ - { - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please select an option", "type": "isLength" }], - "input": { - "options": [ - { - "value": "Yes", - "text": "Yes", - "conditionalQuestions": [ - { - "question": "If yes, please provide more details", - "input": { "type": "textareaInput" }, - "questionId": "safedatadataflowdatatransferedoutsideukdetails" - } - ] - }, - { "value": "No", "text": "No" } + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please select an option" + }, + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + }, + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "input": { + "type": "radioOptionsInput", + "label": "Will the data be transferred outside of the United Kingdom?", + "options": [ + { + "conditionalQuestions": [ + { + "validations": [ + { + "type": "isLength", + "params": [ + 1 ], - "label": "Will the data be transferred outside of the United Kingdom?", - "type": "radioOptionsInput", - "required": true - }, - "questionId": "safedatadataflowdatatransferedoutsideuk", - "question": "Will the data be transferred outside of the United Kingdom?", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safedatadataflowregionsdataprocessed", - "input": { - "type": "checkboxOptionsInput", - "label": "Please specify the regions where data will be processed.", - "options": [ - { "text": "England/Wales", "value": "England/Wales" }, - { "value": "United Kingdom", "text": "United Kingdom" }, - { "value": "European Economic Area", "text": "European Economic Area" }, - { "value": "Other", "text": "Other" } - ] - }, - "question": "Please specify the regions where data will be processed.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { "type": "textareaInput" }, - "questionId": "safedatadataflowdetailedinformation", - "question": "Please provide detailed information on data flows", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "questionId": "safedatadataflowdiagramenclosed", - "input": { - "options": [{ "value": "I have enclosed a copy of the dataflow", "text": "I have enclosed a copy of the dataflow" }], - "label": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", - "type": "checkboxOptionsInput" + "message": "Please enter a value" + } + ], + "input": { + "required": true, + "type": "textareaInput" }, - "question": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "question": "If yes, please provide more details", + "questionId": "safedatadataflowdatatransferedoutsideukdetails" + } + ], + "value": "Yes", + "text": "Yes" + }, + { + "text": "No", + "value": "No" + } ], - "questionSetHeader": "Dataflow", - "questionSetId": "safesettings-dataflow" - }, - { - "questions": [ - { - "question": "How will proposal findings be disseminated, to what audience and in what format?", - "questionId": "safeoutputsoutputsdisseminationplansproposalfindings", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], - "input": { "required": true, "type": "textareaInput" }, - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "message": "Please enter a value", "type": "isLength" }], - "input": { "type": "textareaInput", "required": true }, - "questionId": "safeoutputsoutputsdisseminationplansmilestones", - "question": "Please include any milestones for outputs dissemination.", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], - "input": { "type": "textareaInput", "required": true }, - "questionId": "safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy", - "question": "What steps will be taken to ensure that individuals cannot be identified? Please describe what disclosure control policy will be applied.", - "lockedQuestion": 1, - "defaultQuestion": 0 - } - ], - "questionSetId": "safeoutputs-outputsdisseminationplans", - "questionSetHeader": "Outputs dissemination plans" - }, - { - "questionSetHeader": "Retention", - "questionSetId": "safeoutputs-retention", - "questions": [ - { - "question": "Please state the date until which you will retain the data", - "validations": [{ "params": [{ "$numberDouble": "1.0" }], "type": "isLength", "message": "Please enter a value" }], - "questionId": "safeoutputs-dataretention-retaindatadate", - "input": { "required": true, "type": "datePickerCustom" }, - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { "type": "textareaInput" }, - "questionId": "safeoutputsdataretentionretaindatadatereason", - "question": "Please indicate the reason for this date", - "lockedQuestion": 1, - "defaultQuestion": 0 - }, - { - "input": { "type": "textareaInput" }, - "questionId": "safeoutputsdataretentionretaindataextensionpermissions", - "question": "Please provide details of any permissions that will need to apply for an extension during this period in order to retain a legal basis to hold the data (e.g. section 251)", - "lockedQuestion": 1, - "defaultQuestion": 0 - } + "required": true + }, + "lockedQuestion": 1, + "question": "Will the data be transferred outside of the United Kingdom?", + "questionId": "safedatadataflowdatatransferedoutsideuk", + "defaultQuestion": 1, + "guidance": "" + }, + { + "defaultQuestion": 1, + "questionId": "safedatadataflowregionsdataprocessed", + "question": "Please specify the regions where data will be processed.", + "guidance": "Please indicate if data will be transferred outside of the European Economic Area, it must be stated where to and details given of how that will be in compliance with the Data Protection Act 2018.\n\n If data are to be stored or processed outside of England/Wales, it may be that you will need to provide further assurance to support your application\n", + "lockedQuestion": 0, + "input": { + "type": "checkboxOptionsInput", + "label": "Please specify the regions where data will be processed.", + "options": [ + { + "value": "England/Wales", + "text": "England/Wales" + }, + { + "value": "United Kingdom", + "text": "United Kingdom" + }, + { + "value": "European Economic Area", + "text": "European Economic Area" + }, + { + "text": "Other", + "value": "Other" + } ] + } + }, + { + "questionId": "safedatadataflowdetailedinformation", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "question": "Please provide detailed information on data flows", + "guidance": "" + }, + { + "lockedQuestion": 0, + "input": { + "type": "checkboxOptionsInput", + "label": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", + "options": [ + { + "text": "I have enclosed a copy of the dataflow", + "value": "I have enclosed a copy of the dataflow" + } + ] + }, + "question": "Please include a data flow diagram for the requested data and any additional datasets intended to be linked.", + "guidance": "A data flow diagram is helpful in showing planned data flows of how data will move through the project, whether the data are identifiable or pseudonymised, who has access to, who is responsible for the data at any point, the permissions/consent in place and how it will be kept secure at every stage.\n \n The data flow should describe which organisation (if more than one) will be receiving the data and in what form (anonymised/limited access de-identified/personal). Your data flow should include:\n \n* All locations where the data will be housed/stored\n \n* All transfers of data that will take place between organisations (and premises if an organisation has more than one remises where the data will be housed/stored)\n \n* The format of the data as part of each transfer (anonymised/limited access de-identified/personal)\n \n* If applicable, where the data will undergo any linkages to other data sets\n\n Please attach your data flow diagram to this application as an additional file. Please display only the data requested and any linked datasets, and not the entire project data flow. Documents can be uploaded in the Additional Files section of this form.\n", + "questionId": "safedatadataflowdiagramenclosed", + "defaultQuestion": 1 + } + ] + }, + { + "questionSetHeader": "Outputs dissemination plans", + "questionSetId": "safeoutputs-outputsdisseminationplans", + "questions": [ + { + "defaultQuestion": 1, + "questionId": "safeoutputsoutputsdisseminationplansproposalfindings", + "question": "How will proposal findings be disseminated, to what audience and in what format?", + "lockedQuestion": 1, + "input": { + "type": "textareaInput", + "required": true + }, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "guidance": "" + }, + { + "question": "Please include any milestones for outputs dissemination.", + "input": { + "type": "textareaInput", + "required": true + }, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "lockedQuestion": 1, + "defaultQuestion": 1, + "questionId": "safeoutputsoutputsdisseminationplansmilestones", + "guidance": "" + }, + { + "defaultQuestion": 1, + "questionId": "safeoutputsoutputsdisseminationplansdisclosurecontrolpolicy", + "question": "What steps will be taken to ensure that individuals cannot be identified? Please describe what disclosure control policy will be applied.", + "lockedQuestion": 1, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "input": { + "required": true, + "type": "textareaInput" + }, + "guidance": "" + }, + { + "questionId": "safeoutputsoutputsdisseminationplansoutputmanipulation", + "defaultQuestion": 1, + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "question": "Could the outputs of the use of this data be manipulated to serve purposes that are not in the public interest?", + "guidance": "A data use purpose cannot be beneficial where the harm which would result from its output outweighs the public benefit.\n\nThe benefit aspect requires that the purpose must be beneficial and any risks that result from the purpose must not outweigh any benefits. The public aspect requires that the purpose should benefit the public, or a sufficient section of the public, and any private or commercial benefit should be proportionately balanced with the benefits to the public. Any private benefit should be incidental to the public benefit. Please indicate if outputs of the use of this data could serve for commercial interests or other purposes that are not in the public interest.\n\nPlease note that solely commercial benefits will not satisfy the public aspect of the public benefit requirement.\n\n---\n\n\n**This guidance is aligned with: ** \n\n\n![Test image](https://storage.googleapis.com/hdruk-gateway_non-prod-cms/web-assets/NDG-logo-small.png)" } - ] + ] + }, + { + "questionSetId": "safeoutputs-retention", + "questionSetHeader": "Retention", + "questions": [ + { + "question": "Please state the date until which you will retain the data", + "guidance": "Please state how long you intend to retain the data relating to your proposal.", + "lockedQuestion": 0, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "input": { + "required": true, + "type": "datePickerCustom" + }, + "defaultQuestion": 1, + "questionId": "safeoutputs-dataretention-retaindatadate" + }, + { + "question": "Please indicate the reason for this date", + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "defaultQuestion": 1, + "questionId": "safeoutputsdataretentionretaindatadatereason", + "guidance": "" + }, + { + "lockedQuestion": 0, + "input": { + "type": "textareaInput" + }, + "question": "Please provide details of any permissions that will need to apply for an extension during this period in order to retain a legal basis to hold the data (e.g. section 251)", + "questionId": "safeoutputsdataretentionretaindataextensionpermissions", + "defaultQuestion": 1, + "guidance": "" + }, + { + "question": "Please confirm you will only use the data provided for the purpose specified in the Data Sharing Agreement", + "input": { + "options": [ + { + "value": "I confirm", + "text": "I confirm" + } + ], + "label": "Please confirm you will only use the data provided for the purpose specified in the Data Sharing Agreement", + "type": "checkboxOptionsInput" + }, + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "safeoutputsdataretentionconfirmpurpose", + "guidance": "" + } + ] + }, + { + "questionSetHeader": "Archiving", + "questionSetId": "safeoutputs-archiving", + "questions": [ + { + "guidance": "Please provide details of how the data/files will be disposed of at the end of the period specified above. You might refer to any relevant disposal or destruction policies held by your organisation, by summarising the relevant section from the policy or including a URL and indicating which section is relevant.", + "question": "What method of destruction will be used when this period has expired?", + "lockedQuestion": 0, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "input": { + "required": true, + "type": "textareaInput" + }, + "defaultQuestion": 1, + "questionId": "safeoutputsdataretentiondestructionmethod" + }, + { + "questionId": "safeoutputsdataretentiondestructionevidence", + "defaultQuestion": 1, + "input": { + "type": "textareaInput" + }, + "lockedQuestion": 0, + "guidance": "Please confirm what evidence will be obtained following that destruction, if appropriate.", + "question": "What evidence will be provided that destruction has occurred and when?" + } + ] + }, + { + "questions": [ + { + "question": "Full name", + "guidance": "Please provide the name of the person to whom we should send the invoice.", + "input": { + "type": "textInput", + "required": true + }, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "lockedQuestion": 0, + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactfullname" + }, + { + "question": "Job title", + "guidance": "Please provide the job title for the person to whom we should send the invoice.", + "lockedQuestion": 0, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "input": { + "type": "textInput", + "required": true + }, + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactjobtitle" + }, + { + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactaddressline1", + "guidance": "Please provide the business address for the person to whom we should send the invoice.", + "question": "Registered address (line 1)", + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "lockedQuestion": 0 + }, + { + "input": { + "type": "textInput" + }, + "lockedQuestion": 0, + "question": "Registered address (line 2)", + "guidance": "Please include the organisation's business address.", + "questionId": "additionalinformationinvoicecontactaddressline2", + "defaultQuestion": 1 + }, + { + "questionId": "additionalinformationinvoicecontactcity", + "defaultQuestion": 1, + "lockedQuestion": 0, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "input": { + "type": "textInput", + "required": true + }, + "question": "City", + "guidance": "Please specify the city where the organisation is located" + }, + { + "guidance": "Please include the organisation's business address postcode", + "question": "Postcode", + "lockedQuestion": 0, + "input": { + "type": "textInput", + "required": true + }, + "validations": [ + { + "message": "Please enter a value", + "params": [ + 1 + ], + "type": "isLength" + } + ], + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactpostcode" + }, + { + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactcountry", + "guidance": "Please specify the country where the organisation is located.", + "question": "Country", + "input": { + "type": "textInput", + "required": true + }, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + } + ], + "lockedQuestion": 0 + }, + { + "questionId": "additionalinformationinvoicecontacttelephone", + "defaultQuestion": 1, + "input": { + "type": "textInput" + }, + "lockedQuestion": 0, + "guidance": "Please provide the telephone number for the person to whom we should send the invoice.", + "question": "Telephone" + }, + { + "guidance": "Please provide an email address for the person to whom we should send the invoice.", + "question": "Email", + "lockedQuestion": 0, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + }, + { + "type": "isEmail" + } + ], + "input": { + "required": true, + "type": "textInput" + }, + "defaultQuestion": 1, + "questionId": "additionalinformationinvoicecontactemail" + } + ], + "questionSetHeader": "Invoice contact", + "questionSetId": "additionalinformationfiles-invoicecontact" + }, + { + "questions": [ + { + "defaultQuestion": 1, + "questionId": "additionalinformationdsasignatoryfullname", + "guidance": "Please provide the name of the person who can sign the Data Sharing Agreement on behalf of your organisation.", + "question": "Full name", + "lockedQuestion": 0, + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + } + ], + "input": { + "type": "textInput", + "required": true + } + }, + { + "guidance": "Please provide the job title for the person who can sign the Data Sharing Agreement on behalf of your organisation.", + "question": "Job title", + "lockedQuestion": 0, + "input": { + "type": "textInput", + "required": true + }, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + } + ], + "defaultQuestion": 1, + "questionId": "additionalinformationdsasignatoryjobtitle" + }, + { + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "lockedQuestion": 0, + "question": "Registered address (line 1)", + "guidance": "Please provide the business address for the person who can sign the Data Sharing Agreement on behalf of your organisation.", + "questionId": "additionalinformationdsasignatoryaddressline1", + "defaultQuestion": 1 + }, + { + "questionId": "additionalinformationdsasignatoryaddressline2", + "defaultQuestion": 1, + "lockedQuestion": 0, + "input": { + "type": "textInput" + }, + "question": "Registered address (line 2)", + "guidance": "Please include the organisation's business address." + }, + { + "input": { + "required": true, + "type": "textInput" + }, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + } + ], + "lockedQuestion": 0, + "guidance": "Please specify the city where the organisation is located", + "question": "City", + "questionId": "additionalinformationdsasignatorycity", + "defaultQuestion": 1 + }, + { + "lockedQuestion": 0, + "validations": [ + { + "params": [ + 1 + ], + "type": "isLength", + "message": "Please enter a value" + } + ], + "input": { + "required": true, + "type": "textInput" + }, + "question": "Postcode", + "guidance": "Please include the organisation's business address postcode", + "questionId": "additionalinformationdsasignatorypostcode", + "defaultQuestion": 1 + }, + { + "questionId": "additionalinformationdsasignatorycountry", + "defaultQuestion": 1, + "validations": [ + { + "message": "Please enter a value", + "type": "isLength", + "params": [ + 1 + ] + } + ], + "input": { + "required": true, + "type": "textInput" + }, + "lockedQuestion": 0, + "question": "Country", + "guidance": "Please specify the country where the organisation is located." + }, + { + "question": "Telephone", + "guidance": "Please provide the telephone number for the person who can sign the Data Sharing Agreement on behalf of your organisation.", + "lockedQuestion": 0, + "input": { + "type": "textInput" + }, + "defaultQuestion": 1, + "questionId": "additionalinformationdsasignatorytelephone" + }, + { + "validations": [ + { + "type": "isLength", + "params": [ + 1 + ], + "message": "Please enter a value" + }, + { + "type": "isEmail" + } + ], + "input": { + "required": true, + "type": "textInput" + }, + "lockedQuestion": 0, + "question": "Email", + "guidance": "Please provide an email address for the person who can sign the Data Sharing Agreement on behalf of your organisation.", + "questionId": "additionalinformationdsasignatoryemail", + "defaultQuestion": 1 + } + ], + "questionSetId": "additionalinformationfiles-datasharingagreementsignatory", + "questionSetHeader": "Data Sharing Agreement signatory" + } + ] + }, + "languageCode": "en", + "updatedAt": { + "$date": { + "$numberLong": "1636469643586" + } } -} -git pus \ No newline at end of file + } \ No newline at end of file diff --git a/src/resources/questionbank/__mocks__/questionbank.js b/src/resources/questionbank/__mocks__/questionbank.js index 354f0941..e9c48506 100644 --- a/src/resources/questionbank/__mocks__/questionbank.js +++ b/src/resources/questionbank/__mocks__/questionbank.js @@ -15,7 +15,7 @@ export const globalDocument = { masterSchema: { questionSets: [ { - questionSetId: 'primaryapplicant', + questionSetId: 'safepeople-primaryapplicant', questionSetHeader: 'Primary applicant', questions: [ { diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index d3e7c0aa..155debe3 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -26,64 +26,191 @@ describe('Question Bank Service', function () { dataRequestRepositoryStubCreate.restore(); }); - it('No data request schema exists', async function () { - dataRequestRepositoryStubGet = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); - dataRequestRepositoryStubCreate = sinon - .stub(dataRequestRepository, 'createApplicationFormSchema') - .returns(noSchemaExists.expectedSchema); - - const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - - expect(result.questionStatus).toEqual(noSchemaExists.expectedSchema.questionStatus); - expect(result.guidance).toEqual(noSchemaExists.expectedSchema.guidance); - expect(result.countOfChanges).toEqual(noSchemaExists.expectedSchema.countOfChanges); - expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + describe('getQuestionBankInfo', () => { + it('No data request schema exists', async function () { + dataRequestRepositoryStubGet = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([]); + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(noSchemaExists.expectedSchema); + + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); + + expect(result.questionStatus).toEqual(noSchemaExists.expectedSchema.questionStatus); + expect(result.guidance).toEqual(noSchemaExists.expectedSchema.guidance); + expect(result.countOfChanges).toEqual(noSchemaExists.expectedSchema.countOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + }); + + it('Draft data request schema exists created through the customize form', async function () { + dataRequestRepositoryStubGet = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([draftSchemaNotCreatedThroughForm.dataRequestSchema]); + + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); + + expect(result.questionStatus).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.questionStatus); + expect(result.guidance).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.guidance); + expect(result.countOfChanges).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.countOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + }); + + it('Active data request schema exists created through the customize form', async function () { + dataRequestRepositoryStubGet = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([activeSchemaCreatedThroughForm.dataRequestSchema]); + + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(activeSchemaCreatedThroughForm.expectedSchema); + + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); + + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + expect(result.guidance).toEqual(activeSchemaCreatedThroughForm.expectedSchema.guidance); + expect(result.questionStatus).toEqual(activeSchemaCreatedThroughForm.expectedSchema.questionStatus); + expect(result.countOfChanges).toEqual(activeSchemaCreatedThroughForm.expectedSchema.countOfChanges); + }); + + it('Active data request schema exists not created through the customize form', async function () { + dataRequestRepositoryStubGet = sinon + .stub(dataRequestRepository, 'getApplicationFormSchemas') + .returns([activeSchemaNotCreatedThroughForm.dataRequestSchema]); + + dataRequestRepositoryStubCreate = sinon + .stub(dataRequestRepository, 'createApplicationFormSchema') + .returns(activeSchemaNotCreatedThroughForm.expectedSchema); + + const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); + + expect(result.questionStatus).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.questionStatus); + expect(result.guidance).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.guidance); + expect(result.countOfChanges).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.countOfChanges); + expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + }); }); - it('Draft data request schema exists created through the customize form', async function () { - dataRequestRepositoryStubGet = sinon - .stub(dataRequestRepository, 'getApplicationFormSchemas') - .returns([draftSchemaNotCreatedThroughForm.dataRequestSchema]); - - const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - - expect(result.questionStatus).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.questionStatus); - expect(result.guidance).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.guidance); - expect(result.countOfChanges).toEqual(draftSchemaNotCreatedThroughForm.dataRequestSchema.countOfChanges); - expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); - }); - - it('Active data request schema exists created through the customize form', async function () { - dataRequestRepositoryStubGet = sinon - .stub(dataRequestRepository, 'getApplicationFormSchemas') - .returns([activeSchemaCreatedThroughForm.dataRequestSchema]); - - dataRequestRepositoryStubCreate = sinon - .stub(dataRequestRepository, 'createApplicationFormSchema') - .returns(activeSchemaCreatedThroughForm.expectedSchema); - - const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - - expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); - expect(result.guidance).toEqual(activeSchemaCreatedThroughForm.expectedSchema.guidance); - expect(result.questionStatus).toEqual(activeSchemaCreatedThroughForm.expectedSchema.questionStatus); - expect(result.countOfChanges).toEqual(activeSchemaCreatedThroughForm.expectedSchema.countOfChanges); - }); - - it('Active data request schema exists not created through the customize form', async function () { - dataRequestRepositoryStubGet = sinon - .stub(dataRequestRepository, 'getApplicationFormSchemas') - .returns([activeSchemaNotCreatedThroughForm.dataRequestSchema]); - - dataRequestRepositoryStubCreate = sinon - .stub(dataRequestRepository, 'createApplicationFormSchema') - .returns(activeSchemaNotCreatedThroughForm.expectedSchema); - - const result = await questionBankService.getQuestionBankInfo(questionBank.publisherDocument._id); - - expect(result.questionStatus).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.questionStatus); - expect(result.guidance).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.guidance); - expect(result.countOfChanges).toEqual(activeSchemaNotCreatedThroughForm.expectedSchema.countOfChanges); - expect(result.masterSchema).toEqual(questionBank.globalDocument.masterSchema); + describe('revertChanges', () => { + let publisherStub; + + const updateSchemaStub = sinon.stub(dataRequestRepository, 'updateApplicationFormSchemaById'); + + afterEach(function () { + publisherStub.restore(); + }); + + it('should throw an error if no data request schemas are found for a given publisher', async () => { + publisherStub = sinon.stub(publisherService, 'getPublisher').resolves(); + sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').resolves([]); + + try { + await questionBankService.revertChanges('testId'); + } catch (err) { + expect(err.message).toEqual('This publisher has no data request schemas'); + } + }); + + it('should reset the entire form if no questionSetIds param passed', async () => { + publisherStub = sinon.stub(publisherService, 'getPublisher').resolves(); + dataRequestRepositoryStubGet = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([ + { + _id: '5f3f98068af2ef61552e1d01', + guidance: { + safepeopleprimaryapplicantfullname: 'What is your full name?', + }, + questionStatus: { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 0, + safepeopleprimaryapplicanttelephone: 0, + safepeopleotherindividualsfullname: 0, + safepeopleotherindividualsjobtitle: 0, + safepeopleotherindividualsorganisation: 0, + }, + unpublishedGuidance: ['safepeopleprimaryapplicantfullname'], + }, + { + _id: '5f3f98068af2ef61552e1d02', + guidance: {}, + questionStatus: { + safepeopleprimaryapplicantfullname: 1, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 1, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 1, + safepeopleotherindividualsorganisation: 1, + }, + unpublishedGuidance: [], + }, + ]); + + await questionBankService.revertChanges(questionBank.publisherDocument._id); + + expect( + updateSchemaStub.calledWith('5f3f98068af2ef61552e1d01', { + questionStatus: { + safepeopleprimaryapplicantfullname: 1, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 1, + safepeopleotherindividualsfullname: 1, + safepeopleotherindividualsjobtitle: 1, + safepeopleotherindividualsorganisation: 1, + }, + unpublishedGuidance: [], + guidance: {}, + countOfChanges: 0, + }) + ).toBe(true); + }); + + it('should reset one section if questionSetIds param passed', async () => { + publisherStub = sinon.stub(publisherService, 'getPublisher').resolves(); + dataRequestRepositoryStubGet = sinon.stub(dataRequestRepository, 'getApplicationFormSchemas').returns([ + { + _id: '5f3f98068af2ef61552e1d01', + guidance: { + safepeopleprimaryapplicantfullname: 'What is your full name?', + }, + questionStatus: { + safepeopleprimaryapplicantfullname: 0, + safepeopleprimaryapplicantjobtitle: 0, + safepeopleprimaryapplicanttelephone: 0, + safeprojectprojectdetailstitle: 0, + safeprojectprojectdetailstype: 0, + safeprojectprojectdetailsneworexisting: 0, + }, + unpublishedGuidance: ['safepeopleprimaryapplicantfullname'], + }, + { + _id: '5f3f98068af2ef61552e1d02', + guidance: {}, + questionStatus: { + safepeopleprimaryapplicantfullname: 1, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 1, + safeprojectprojectdetailstitle: 1, + safeprojectprojectdetailstype: 1, + safeprojectprojectdetailsneworexisting: 1, + }, + unpublishedGuidance: [], + }, + ]); + + await questionBankService.revertChanges(questionBank.publisherDocument._id, 'safepeople'); + + expect( + updateSchemaStub.calledWith('5f3f98068af2ef61552e1d01', { + questionStatus: { + safepeopleprimaryapplicantfullname: 1, + safepeopleprimaryapplicantjobtitle: 1, + safepeopleprimaryapplicanttelephone: 1, + safeprojectprojectdetailstitle: 0, + safeprojectprojectdetailstype: 0, + safeprojectprojectdetailsneworexisting: 0, + }, + unpublishedGuidance: [], + guidance: {}, + countOfChanges: 3, + }) + ).toBe(true); + }); }); }); diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index 27ce2746..2e82983d 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -1,3 +1,4 @@ +import { NextPage } from '@hubspot/api-client/lib/codegen/cms/hubdb/api'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; @@ -69,14 +70,9 @@ export default class QuestionbankController extends Controller { async revertChanges(req, res) { try { const { publisherId } = req.params; - const { questionSet } = req.query; + const { page } = req.query; - let target; - if (questionSet) { - target = questionSet; - } - - await this.questionbankService.revertChanges(publisherId, target); + await this.questionbankService.revertChanges(publisherId, page); return res.status(200).json({ success: true }); } catch (err) { diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 1e8283af..01669d99 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -214,11 +214,14 @@ export default class QuestionbankService { } // Revert updates for a given question panel ELSE revert all updates + let countOfChanges = 0; if (target) { const panelQuestions = await this.getPanelQuestions(target); const updates = Object.keys(previousState).filter(key => !panelQuestions.includes(key)); updates.forEach(key => { + if (previousState[key] !== dataRequestSchemas[0].questionStatus[key]) countOfChanges += 1; + previousState[key] = dataRequestSchemas[0].questionStatus[key]; if (dataRequestSchemas[0].unpublishedGuidance.includes(key)) { @@ -235,6 +238,7 @@ export default class QuestionbankService { questionStatus: previousState, unpublishedGuidance, guidance, + countOfChanges, }); return; @@ -260,13 +264,18 @@ export default class QuestionbankService { const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); const questionSets = global.masterSchema.questionSets; - const panelQuestions = questionSets.filter(questionSet => questionSet.questionSetId === target); + const panelQuestions = questionSets.filter(questionSet => questionSet.questionSetId.includes(target)); - if (panelQuestions.length === 0) { - throw new Error('This is not a valid questionSetId'); + if (!panelQuestions) { + throw new Error('Invalid page identifier: ' + target); } - const questionIds = panelQuestions[0].questions.map(question => question.questionId); + let questions = []; + panelQuestions.forEach(panel => { + questions.push(...panel.questions); + }); + + const questionIds = questions.map(question => question.questionId); return questionIds; } diff --git a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js b/src/resources/user/__tests__/getCollaboratorsCohorts.test.js deleted file mode 100644 index bb626688..00000000 --- a/src/resources/user/__tests__/getCollaboratorsCohorts.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import dbHandler from '../../../config/in-memory-db'; -import {mockCohorts} from '../__mocks__/cohorts.data'; - -const {getCollaboratorsCohorts} = require('../user.service'); - - -beforeAll(async () => { - await dbHandler.connect(); - await dbHandler.loadData({ cohorts: mockCohorts }); -}); - -afterAll(async () => { - await dbHandler.clearDatabase(); - await dbHandler.closeDatabase(); -}); - -describe('getCollaboratorsCohorts tests', () => { - it('should return values', async () => { - const currentUserId = 8470291714590257; - const filter = currentUserId ? { uploaders: currentUserId } : {}; - - const result = await getCollaboratorsCohorts(filter, currentUserId); - expect(result.length > 0).toBe(true); - expect(typeof result).toBe('object'); - }); - - it('should return values', async () => { - const currentUserId = null; - const filter = currentUserId ? { uploaders: currentUserId } : {}; - - const result = await getCollaboratorsCohorts(filter, currentUserId); - expect(result.length > 0).toBe(true); - expect(typeof result).toBe('object'); - }); -}); \ No newline at end of file From c001e5056fbf86d1577dcaf9ef4b2d9054348909 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Tue, 31 May 2022 10:49:48 +0100 Subject: [PATCH 100/389] remove unused import --- src/resources/questionbank/questionbank.controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/questionbank/questionbank.controller.js b/src/resources/questionbank/questionbank.controller.js index 2e82983d..f6caff38 100644 --- a/src/resources/questionbank/questionbank.controller.js +++ b/src/resources/questionbank/questionbank.controller.js @@ -1,4 +1,3 @@ -import { NextPage } from '@hubspot/api-client/lib/codegen/cms/hubdb/api'; import Controller from '../base/controller'; import { logger } from '../utilities/logger'; From 48cc69a6fb01370726943456d7d4a42265fd7706 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Jun 2022 11:55:26 +0300 Subject: [PATCH 101/389] enable and disable caching --- .../datarequest/datarequest.controller.js | 39 ++++++++++--------- src/resources/message/message.controller.js | 39 ++++++++++--------- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 661319d8..9704c9a1 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -453,24 +453,27 @@ export default class DataRequestController extends Controller { } // publish the message to Redis PubSub - let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); - - if (accessRecord.applicationStatus === constants.applicationStatuses.SUBMITTED - && publisherDetails['dar-integration']['enabled']) { - const pubSubMessage = { - id: "", - type: "5safes", - publisherInfo: { - id: accessRecord.publisherObj._id, - name: accessRecord.publisherObj.name, - }, - data: { - dataRequestId: accessRecord._id, - createdDate: accessRecord.createdAt, - data: accessRecord.questionAnswers, - } - }; - await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + const cacheEnabled = process.env.CACHE_ENABLED || false; + if(cacheEnabled) { + let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); + + if (accessRecord.applicationStatus === constants.applicationStatuses.SUBMITTED + && publisherDetails['dar-integration']['enabled']) { + const pubSubMessage = { + id: "", + type: "5safes", + publisherInfo: { + id: accessRecord.publisherObj._id, + name: accessRecord.publisherObj.name, + }, + data: { + dataRequestId: accessRecord._id, + createdDate: accessRecord.createdAt, + data: accessRecord.questionAnswers, + } + }; + await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + } } // 11. Return aplication and successful response diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 1a0933c5..50bdad22 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -187,25 +187,28 @@ module.exports = { } // publish the message to Redis PubSub - let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); + const cacheEnabled = process.env.CACHE_ENABLED || false; + if(cacheEnabled) { + let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); - if (publisherDetails['dar-integration']['enabled']) { - const pubSubMessage = { - id: "", - type: "enquiry", - publisherInfo: { - id: publisherDetails._id, - name: publisherDetails.name, - }, - data: { - topicId: topicObj._id, - messageId: message.messageID, - createdDate: message.createdDate, - data: req.body.firstMessage, - - } - }; - await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + if (publisherDetails['dar-integration']['enabled']) { + const pubSubMessage = { + id: "", + type: "enquiry", + publisherInfo: { + id: publisherDetails._id, + name: publisherDetails.name, + }, + data: { + topicId: topicObj._id, + messageId: message.messageID, + createdDate: message.createdDate, + data: req.body.firstMessage, + + } + }; + await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + } } } From 4e558e01bb70245e069993ed88b7e154f1363562 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Jun 2022 13:03:16 +0300 Subject: [PATCH 102/389] enable cache --- src/resources/message/message.controller.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 50bdad22..3fe013cc 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -187,8 +187,8 @@ module.exports = { } // publish the message to Redis PubSub - const cacheEnabled = process.env.CACHE_ENABLED || false; - if(cacheEnabled) { + //const cacheEnabled = process.env.CACHE_ENABLED || false; + //if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); if (publisherDetails['dar-integration']['enabled']) { @@ -207,9 +207,10 @@ module.exports = { } }; + console.log(pubSubMessage); await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); } - } + //} } // 19. Return successful response with message data From efab2d1519c924b6f93c7b5a5dfa54d29735b722 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Jun 2022 13:33:28 +0300 Subject: [PATCH 103/389] rollback cache --- src/resources/message/message.controller.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 3fe013cc..50bdad22 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -187,8 +187,8 @@ module.exports = { } // publish the message to Redis PubSub - //const cacheEnabled = process.env.CACHE_ENABLED || false; - //if(cacheEnabled) { + const cacheEnabled = process.env.CACHE_ENABLED || false; + if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); if (publisherDetails['dar-integration']['enabled']) { @@ -207,10 +207,9 @@ module.exports = { } }; - console.log(pubSubMessage); await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); } - //} + } } // 19. Return successful response with message data From 339c0f2ecf864041972803d1a63fe90ec27fc0f7 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 6 Jun 2022 17:39:12 +0300 Subject: [PATCH 104/389] update checking cache connection --- src/services/cachePubSub/cachePubSubClient.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/cachePubSub/cachePubSubClient.js b/src/services/cachePubSub/cachePubSubClient.js index 615bb322..008dc33e 100644 --- a/src/services/cachePubSub/cachePubSubClient.js +++ b/src/services/cachePubSub/cachePubSubClient.js @@ -4,7 +4,9 @@ const client = createClient({ }); export const publishMessageToChannel = async (channel, message) => { - await client.connect(); + if (!client.isOpen) { + await client.connect(); + } client.on("connect", () => console.log("Redis cache is ready")); client.on("error", (err) => console.log('Redis Client Error', err)); From 21f51cd55f13df896315129dec8f70bbb706970f Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Thu, 9 Jun 2022 12:40:11 +0300 Subject: [PATCH 105/389] try and catch --- src/services/cachePubSub/cachePubSubClient.js | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/services/cachePubSub/cachePubSubClient.js b/src/services/cachePubSub/cachePubSubClient.js index 008dc33e..30fa7f52 100644 --- a/src/services/cachePubSub/cachePubSubClient.js +++ b/src/services/cachePubSub/cachePubSubClient.js @@ -1,16 +1,23 @@ import { createClient } from 'redis'; -const client = createClient({ - url: process.env.CACHE_URL -}); export const publishMessageToChannel = async (channel, message) => { - if (!client.isOpen) { - 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')); + try { + const client = createClient({ + url: process.env.CACHE_URL + }); - await client.publish(channel, message); + if (!client.isOpen) { + 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')); + + await client.publish(channel, message); + + } catch (e) { + console.log(e); + throw new Error(e.message); + } } \ No newline at end of file From 5448f32a8f42f9a23a0b39d5cb71f8275bdeabe9 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 13 Jun 2022 10:05:47 +0100 Subject: [PATCH 106/389] move to redis pubsub to gcp pubsub with retry policy --- .../datarequest/datarequest.controller.js | 4 +- src/resources/message/message.controller.js | 6 +- src/services/cachePubSub/cachePubSubClient.js | 1 + src/services/google/PubSubWithRetryService.js | 70 +++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/services/google/PubSubWithRetryService.js diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 9704c9a1..979c5dab 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -16,7 +16,7 @@ import { logger } from '../utilities/logger'; import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; import { dataUseRegisterController } from '../dataUseRegister/dependency'; -import { publishMessageToChannel } from '../../services/cachePubSub/cachePubSubClient'; +import { publishMessageToPubSub } from '../../services/google/PubSubService'; const logCategory = 'Data Access Request'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); @@ -472,7 +472,7 @@ export default class DataRequestController extends Controller { data: accessRecord.questionAnswers, } }; - await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + await publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } } diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 50bdad22..df8d0566 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -9,7 +9,8 @@ import { Data as ToolModel } from '../tool/data.model'; import constants from '../utilities/constants.util'; import { dataRequestService } from '../datarequest/dependency'; import { activityLogService } from '../activitylog/dependency'; -import { publishMessageToChannel } from '../../services/cachePubSub/cachePubSubClient'; +import { publishMessageToPubSub } from '../../services/google/PubSubService'; +import { publishMessageWithRetryToPubSub } from '../../services/google/PubSubWithRetryService'; import { PublisherModel } from '../publisher/publisher.model'; const topicController = require('../topic/topic.controller'); @@ -207,7 +208,8 @@ module.exports = { } }; - await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); + // await publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); + await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } } diff --git a/src/services/cachePubSub/cachePubSubClient.js b/src/services/cachePubSub/cachePubSubClient.js index 30fa7f52..215b7c1e 100644 --- a/src/services/cachePubSub/cachePubSubClient.js +++ b/src/services/cachePubSub/cachePubSubClient.js @@ -1,5 +1,6 @@ import { createClient } from 'redis'; +// await publishMessageToChannel(process.env.CACHE_CHANNEL, JSON.stringify(pubSubMessage)); export const publishMessageToChannel = async (channel, message) => { try { const client = createClient({ diff --git a/src/services/google/PubSubWithRetryService.js b/src/services/google/PubSubWithRetryService.js new file mode 100644 index 00000000..2bc7822f --- /dev/null +++ b/src/services/google/PubSubWithRetryService.js @@ -0,0 +1,70 @@ +// example params in .env file: +// PUBSUB_PROJECT_ID='hdruk-gateway-dev' +// PUBSUB_TOPIC_ENQUIRY='enquiry' +// PUBSUB_SUBSCRIPTION_ID='enquiry-sub' + +const {v1} = require('@google-cloud/pubsub'); + +const publisherClient = new v1.PublisherClient({ + projectId: process.env.PUBSUB_PROJECT_ID, +}); + +export const publishMessageWithRetryToPubSub = async (topicName, message) => { + const formattedTopic = publisherClient.projectTopicPath( + process.env.PUBSUB_PROJECT_ID, + topicName + ); + + const dataBuffer = Buffer.from(JSON.stringify(message)); + const messagesElement = { + data: dataBuffer, + }; + const messages = [messagesElement]; + + // Build the request + const request = { + topic: formattedTopic, + messages, + }; + + // Retry settings control how the publisher handles retryable failures. Default values are shown. + // The `retryCodes` array determines which grpc errors will trigger an automatic retry. + // The `backoffSettings` object lets you specify the behaviour of retries over time. + const retrySettings = { + retryCodes: [ + 10, // 'ABORTED' + 1, // 'CANCELLED', + 4, // 'DEADLINE_EXCEEDED' + 13, // 'INTERNAL' + 8, // 'RESOURCE_EXHAUSTED' + 14, // 'UNAVAILABLE' + 2, // 'UNKNOWN' + ], + backoffSettings: { + // The initial delay time, in milliseconds, between the completion + // of the first failed request and the initiation of the first retrying request. + initialRetryDelayMillis: 100, + // The multiplier by which to increase the delay time between the completion + // of failed requests, and the initiation of the subsequent retrying request. + retryDelayMultiplier: 1.3, + // The maximum delay time, in milliseconds, between requests. + // When this value is reached, retryDelayMultiplier will no longer be used to increase delay time. + maxRetryDelayMillis: 60000, + // The initial timeout parameter to the request. + initialRpcTimeoutMillis: 5000, + // The multiplier by which to increase the timeout parameter between failed requests. + rpcTimeoutMultiplier: 1.0, + // The maximum timeout parameter, in milliseconds, for a request. When this value is reached, + // rpcTimeoutMultiplier will no longer be used to increase the timeout. + maxRpcTimeoutMillis: 600000, + // The total time, in milliseconds, starting from when the initial request is sent, + // after which an error will be returned, regardless of the retrying attempts made meanwhile. + totalTimeoutMillis: 600000, + }, + }; + + const [response] = await publisherClient.publish(request, { + retry: retrySettings, + }); + console.log(`Message ${response.messageIds} published.`); +} \ No newline at end of file From 71ee477f4921e08b4ff39bf54b149c1453867926 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 13 Jun 2022 10:08:31 +0100 Subject: [PATCH 107/389] move --- src/resources/datarequest/datarequest.controller.js | 4 ++-- src/resources/message/message.controller.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 979c5dab..7b523560 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -16,7 +16,7 @@ import { logger } from '../utilities/logger'; import { UserModel } from '../user/user.model'; import { PublisherModel } from '../publisher/publisher.model'; import { dataUseRegisterController } from '../dataUseRegister/dependency'; -import { publishMessageToPubSub } from '../../services/google/PubSubService'; +import { publishMessageWithRetryToPubSub } from '../../services/google/PubSubWithRetryService'; const logCategory = 'Data Access Request'; const bpmController = require('../bpmnworkflow/bpmnworkflow.controller'); @@ -472,7 +472,7 @@ export default class DataRequestController extends Controller { data: accessRecord.questionAnswers, } }; - await publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); + await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } } diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index df8d0566..557c787d 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -9,7 +9,6 @@ import { Data as ToolModel } from '../tool/data.model'; import constants from '../utilities/constants.util'; import { dataRequestService } from '../datarequest/dependency'; import { activityLogService } from '../activitylog/dependency'; -import { publishMessageToPubSub } from '../../services/google/PubSubService'; import { publishMessageWithRetryToPubSub } from '../../services/google/PubSubWithRetryService'; import { PublisherModel } from '../publisher/publisher.model'; @@ -208,7 +207,6 @@ module.exports = { } }; - // await publishMessageToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } } From f02c56f1296e8d61b2c0f51da1ccbd977f28e626 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Mon, 13 Jun 2022 10:09:25 +0100 Subject: [PATCH 108/389] small update --- src/resources/datarequest/datarequest.controller.js | 2 +- src/resources/message/message.controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 7b523560..010ed4c4 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -452,7 +452,7 @@ export default class DataRequestController extends Controller { bpmController.postStartPreReview(bpmContext); } - // publish the message to Redis PubSub + // publish the message to GCP PubSub const cacheEnabled = process.env.CACHE_ENABLED || false; if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 557c787d..3252dba5 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -186,7 +186,7 @@ module.exports = { ); } - // publish the message to Redis PubSub + // publish the message to GCP PubSub const cacheEnabled = process.env.CACHE_ENABLED || false; if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); From ed890b03a8d3f5225a5adff52f124931d974eed6 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Mon, 13 Jun 2022 18:10:27 +0100 Subject: [PATCH 109/389] fix BC redirect issue --- src/resources/auth/strategies/oidc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/resources/auth/strategies/oidc.js b/src/resources/auth/strategies/oidc.js index 3748e8fd..53fe29c5 100644 --- a/src/resources/auth/strategies/oidc.js +++ b/src/resources/auth/strategies/oidc.js @@ -69,6 +69,9 @@ const strategy = app => { app.get( '/auth/oidc/callback', (req, res, next) => { + if (req.query.target_link_uri) { + req.param.returnpage = req.query.target_link_uri; + } passport.authenticate('oidc', (err, user) => { req.auth = { err: err, From 148ab2ed401a4c71519cd3a746ac4c8b51b04254 Mon Sep 17 00:00:00 2001 From: kandaj Date: Wed, 15 Jun 2022 13:35:38 +0100 Subject: [PATCH 110/389] email template update --- .../utilities/emailGenerator.util.js | 110 +++++++++--------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index ecc44db9..2b89a481 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -16,16 +16,16 @@ let questionList = []; let excludedQuestionSetIds = ['addRepeatableSection', 'removeRepeatableSection']; let autoCompleteLookups = { fullname: ['email'] }; let transporterOptions = { - host: process.env.MAIL_HOST, - port: process.env.MAIL_PORT, - auth: { - user: process.env.MAIL_USERNAME, - pass: process.env.MAIL_PASSWORD, - }, - pool: true, - maxConnections: 1, - rateDelta: 20000, - rateLimit: 5, + host: process.env.MAIL_HOST, + port: process.env.MAIL_PORT, + auth: { + user: process.env.MAIL_USERNAME, + pass: process.env.MAIL_PASSWORD, + }, + pool: true, + maxConnections: 1, + rateDelta: 20000, + rateLimit: 5, }; let transporter = nodemailer.createTransport(transporterOptions); @@ -667,6 +667,7 @@ const _displayDataUseRegisterDashboardLink = () => { const _generateDARStatusChangedEmail = options => { let { id, applicationStatus, applicationStatusDesc, projectId, projectName, publisher, datasetTitles, dateSubmitted, applicants } = options; + console.log(applicationStatus); let body = `
{ @@ -2263,9 +2264,9 @@ const _generateMessageNotification = options => { }; const _generateMessageCreatorNotification = options => { - let { firstMessage, firstname, lastname, messageDescription, openMessagesLink } = options; + let { firstMessage, firstname, lastname, messageDescription, openMessagesLink } = options; - let body = `
+ let body = `
- Your data access request for ${projectName || datasetTitles} has been approved with conditions by ${publisher}. + Your data access request for ${projectName || datasetTitles} has been ${applicationStatus} by ${publisher}. Summary information about your approved project will be included in the Gateway data use register. You will be notified as soon as this becomes visible and searchable on the Gateway.
{ @@ -2295,7 +2298,7 @@ const _generateMessageCreatorNotification = options => {

Dear ${firstname} ${lastname},

Thank you for submitting an enquiry about ${firstMessage.datasetsRequested[0].name}.

-

Your enquiry has been sent to ${firstMessage.datasetsRequested[0].publisher} who will reply in due course. If you have not received a response after 10 working days, or if you have any queries or concerns about the Gateway, please email enquiries@hdruk.ac.uk and a member of the HDR UK team will get in touch with you.

+

Your enquiry has been sent to ${ + firstMessage.datasetsRequested[0].publisher + } who will reply in due course. If you have not received a response after 10 working days, or if you have any queries or concerns about the Gateway, please email enquiries@hdruk.ac.uk and a member of the HDR UK team will get in touch with you.

${messageDescription.replace(/\n/g, '
')}

`; - return body; + return body; }; const _generateEntityNotification = options => { @@ -2554,7 +2557,7 @@ ${_displayDataUseRegisterDashboardLink()} * @param {Object} context */ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, attachments = []) => { - const recipients = [...new Map(to.map(item => [item['email'], item])).values()]; + const recipients = [...new Map(to.map(item => [item['email'], item])).values()]; // 3. Build each email object for SendGrid extracting email addresses from user object with unique unsubscribe link (to) for (let recipient of recipients) { let body = _generateEmailHeader + html + _generateEmailFooter(recipient, allowUnsubscribe); @@ -2566,45 +2569,44 @@ const _sendEmail = async (to, from, subject, html, allowUnsubscribe = true, atta 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); - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(error); - } - + // 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); + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(error); + } } }; -const _sendEmailSmtp = async (message) => { - 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); - Sentry.addBreadcrumb({ - category: 'SendGrid', - message: 'Sending email failed', - level: Sentry.Severity.Warning, - }); - Sentry.captureException(error); - } -} +const _sendEmailSmtp = async message => { + 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); + Sentry.addBreadcrumb({ + category: 'SendGrid', + message: 'Sending email failed', + level: Sentry.Severity.Warning, + }); + Sentry.captureException(error); + } +}; /** * [_sendIntroEmail] @@ -2613,7 +2615,7 @@ const _sendEmailSmtp = async (message) => { * @param {Object} message to from, templateId */ const _sendIntroEmail = msg => { - _sendEmailSmtp(msg); + _sendEmailSmtp(msg); }; const _generateEmailHeader = ` @@ -2736,7 +2738,7 @@ export default { //generateMetadataOnboardingUnArchived: _generateMetadataOnboardingUnArchived, //Messages generateMessageNotification: _generateMessageNotification, - generateMessageCreatorNotification: _generateMessageCreatorNotification, + generateMessageCreatorNotification: _generateMessageCreatorNotification, generateEntityNotification: _generateEntityNotification, //ActivityLog generateActivityLogManualEventCreated: _generateActivityLogManualEventCreated, From f707665ae9f0340fc6fbdb992111e5fe4b1c3020 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 15 Jun 2022 14:18:58 +0100 Subject: [PATCH 111/389] updated search filter behaviour for datasets --- src/config/server.js | 2 +- .../dataUseRegister.controller.js | 4 +- src/resources/search/search.repository.js | 6 +- src/resources/search/search.router.js | 47 ++++---- .../search/searchFilter.controller.js | 102 ++++++++++++++++++ ...{filter.route.js => searchFilter.route.js} | 79 ++++++-------- src/resources/utilities/constants.util.js | 12 +++ 7 files changed, 178 insertions(+), 74 deletions(-) create mode 100644 src/resources/search/searchFilter.controller.js rename src/resources/search/{filter.route.js => searchFilter.route.js} (62%) diff --git a/src/config/server.js b/src/config/server.js index 8711cc88..6318c634 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -198,7 +198,7 @@ app.use('/api/v1/reviews', require('../resources/tool/review.route')); app.use('/api/v1/relatedobject/', require('../resources/relatedobjects/relatedobjects.route')); app.use('/api/v1/accounts', require('../resources/account/account.route')); -app.use('/api/v1/search/filter', require('../resources/search/filter.route')); +app.use('/api/v1/search/filter', require('../resources/search/searchFilter.route')); app.use('/api/v1/search', require('../resources/search/search.router')); // tools projects people app.use('/api/v1/linkchecker', require('../resources/linkchecker/linkchecker.router')); diff --git a/src/resources/dataUseRegister/dataUseRegister.controller.js b/src/resources/dataUseRegister/dataUseRegister.controller.js index 87c52a6e..03903dfe 100644 --- a/src/resources/dataUseRegister/dataUseRegister.controller.js +++ b/src/resources/dataUseRegister/dataUseRegister.controller.js @@ -267,7 +267,7 @@ export default class DataUseRegisterController extends Controller { } let searchQuery = { $and: [{ activeflag: 'active' }] }; - searchQuery = getObjectFilters(searchQuery, req, 'dataUseRegister'); + searchQuery = getObjectFilters(searchQuery, req.query, 'dataUseRegister'); const aggregateQuery = [ { @@ -380,7 +380,7 @@ export default class DataUseRegisterController extends Controller { const team = await TeamModel.findById(dataUseRegister.publisher.toString()); if (team.notifications.length > 0 && team.notifications[0].optIn) { team.notifications[0].subscribedEmails.map(teamEmail => { - teamEmailNotification.push({email: teamEmail}); + teamEmailNotification.push({ email: teamEmail }); }); } const dataUseTeamMembers = teamController.getTeamMembersByRole(adminTeam, constants.roleTypes.ADMIN_DATA_USE); diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index 65b688d8..99a00ba1 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -860,13 +860,13 @@ export function getMyObjectsCount(type, searchAll, searchQuery, authorID) { }); } -export function getObjectFilters(searchQueryStart, req, type) { +export function getObjectFilters(searchQueryStart, queryParams, type) { let searchQuery = JSON.parse(JSON.stringify(searchQueryStart)); // iterate over query string keys - for (const key of Object.keys(req.query)) { + for (const key of Object.keys(queryParams)) { try { - const filterValues = req.query[key].split('::'); + const filterValues = queryParams[key].split('::'); // check mapper for query type // let filterNode = findNodeInTree(`${type}Filters`, key); let filterNode; diff --git a/src/resources/search/search.router.js b/src/resources/search/search.router.js index 82bca413..cc58bd47 100644 --- a/src/resources/search/search.router.js +++ b/src/resources/search/search.router.js @@ -29,7 +29,14 @@ router.get('/', async (req, res) => { let searchAll = false; if (searchString.length > 0) { - searchQuery['$and'].push({ $text: { $search: `'${searchString.split(" ").map(item => item.replace(/^/,"\"").replace(/$/,"\"")).join(" ")}'` }}); + searchQuery['$and'].push({ + $text: { + $search: `'${searchString + .split(' ') + .map(item => item.replace(/^/, '"').replace(/$/, '"')) + .join(' ')}'`, + }, + }); } else { searchAll = true; } @@ -62,7 +69,7 @@ router.get('/', async (req, res) => { getObjectResult( 'dataset', searchAll, - getObjectFilters(searchQuery, req, 'dataset'), + getObjectFilters(searchQuery, req.query, 'dataset'), req.query.datasetIndex || 0, req.query.maxResults || 40, req.query.datasetSort || '' @@ -70,7 +77,7 @@ router.get('/', async (req, res) => { getObjectResult( 'tool', searchAll, - getObjectFilters(searchQuery, req, 'tool'), + getObjectFilters(searchQuery, req.query, 'tool'), req.query.toolIndex || 0, req.query.maxResults || 40, req.query.toolSort || '', @@ -80,7 +87,7 @@ router.get('/', async (req, res) => { getObjectResult( 'project', searchAll, - getObjectFilters(searchQuery, req, 'project'), + getObjectFilters(searchQuery, req.query, 'project'), req.query.projectIndex || 0, req.query.maxResults || 40, req.query.projectSort || '', @@ -90,7 +97,7 @@ router.get('/', async (req, res) => { getObjectResult( 'paper', searchAll, - getObjectFilters(searchQuery, req, 'paper'), + getObjectFilters(searchQuery, req.query, 'paper'), req.query.paperIndex || 0, req.query.maxResults || 40, req.query.paperSort || '', @@ -101,7 +108,7 @@ router.get('/', async (req, res) => { getObjectResult( 'course', searchAll, - getObjectFilters(searchQuery, req, 'course'), + getObjectFilters(searchQuery, req.query, 'course'), req.query.courseIndex || 0, req.query.maxResults || 40, 'startdate', @@ -111,7 +118,7 @@ router.get('/', async (req, res) => { getObjectResult( 'collection', searchAll, - getObjectFilters(searchQuery, req, 'collection'), + getObjectFilters(searchQuery, req.query, 'collection'), req.query.collectionIndex || 0, req.query.maxResults || 40, req.query.collectionSort || '' @@ -119,7 +126,7 @@ router.get('/', async (req, res) => { getObjectResult( 'dataUseRegister', searchAll, - getObjectFilters(searchQuery, req, 'dataUseRegister'), + getObjectFilters(searchQuery, req.query, 'dataUseRegister'), req.query.dataUseRegisterIndex || 0, req.query.maxResults || 40, req.query.dataUseRegisterSort || '' @@ -130,7 +137,7 @@ router.get('/', async (req, res) => { results = await getObjectResult( entityType, searchAll, - getObjectFilters(searchQuery, req, entityType), + getObjectFilters(searchQuery, req.query, entityType), req.query[`${entityType}Index`] || 0, req.query.maxResults || 40, sort @@ -138,14 +145,14 @@ router.get('/', async (req, res) => { } const summaryCounts = await Promise.all([ - getObjectCount('dataset', searchAll, getObjectFilters(searchQuery, req, 'dataset')), - getObjectCount('tool', searchAll, getObjectFilters(searchQuery, req, 'tool')), - getObjectCount('project', searchAll, getObjectFilters(searchQuery, req, 'project')), - getObjectCount('paper', searchAll, getObjectFilters(searchQuery, req, 'paper')), + getObjectCount('dataset', searchAll, getObjectFilters(searchQuery, req.query, 'dataset')), + getObjectCount('tool', searchAll, getObjectFilters(searchQuery, req.query, 'tool')), + getObjectCount('project', searchAll, getObjectFilters(searchQuery, req.query, 'project')), + getObjectCount('paper', searchAll, getObjectFilters(searchQuery, req.query, 'paper')), getObjectCount('person', searchAll, searchQuery), - getObjectCount('course', searchAll, getObjectFilters(searchQuery, req, 'course')), - getObjectCount('collection', searchAll, getObjectFilters(searchQuery, req, 'collection')), - getObjectCount('dataUseRegister', searchAll, getObjectFilters(searchQuery, req, 'dataUseRegister')), + getObjectCount('course', searchAll, getObjectFilters(searchQuery, req.query, 'course')), + getObjectCount('collection', searchAll, getObjectFilters(searchQuery, req.query, 'collection')), + getObjectCount('dataUseRegister', searchAll, getObjectFilters(searchQuery, req.query, 'dataUseRegister')), ]); const summary = { @@ -162,10 +169,10 @@ router.get('/', async (req, res) => { let myEntitiesSummary = {}; if (req.query.form === 'true') { const summaryMyEntityCounts = await Promise.all([ - getMyObjectsCount('tool', searchAll, getObjectFilters(searchQuery, req, 'tool'), authorID), - getMyObjectsCount('project', searchAll, getObjectFilters(searchQuery, req, 'project'), authorID), - getMyObjectsCount('paper', searchAll, getObjectFilters(searchQuery, req, 'paper'), authorID), - getMyObjectsCount('course', searchAll, getObjectFilters(searchQuery, req, 'course'), authorID), + getMyObjectsCount('tool', searchAll, getObjectFilters(searchQuery, req.query, 'tool'), authorID), + getMyObjectsCount('project', searchAll, getObjectFilters(searchQuery, req.query, 'project'), authorID), + getMyObjectsCount('paper', searchAll, getObjectFilters(searchQuery, req.query, 'paper'), authorID), + getMyObjectsCount('course', searchAll, getObjectFilters(searchQuery, req.query, 'course'), authorID), ]); myEntitiesSummary = { diff --git a/src/resources/search/searchFilter.controller.js b/src/resources/search/searchFilter.controller.js new file mode 100644 index 00000000..9105bd8e --- /dev/null +++ b/src/resources/search/searchFilter.controller.js @@ -0,0 +1,102 @@ +import { isEqual, isEmpty, cloneDeep } from 'lodash'; + +import searchUtil from './util/search.util'; +import { getObjectFilters } from './search.repository'; +import constantsUtil from '../utilities/constants.util'; +import { + datasetFilters, + toolFilters, + projectFilters, + paperFilters, + collectionFilters, + courseFilters, + dataUseRegisterFilters, +} from '../filters/filters.mapper'; +import { findNodeInTree } from '../filters/utils/filters.util'; + +export default class SearchFilterController { + constructor(filtersService) { + this.filtersService = filtersService; + } + + /** Get the relevant filters for a given query */ + getSearchFilters = async (req, res) => { + const query = req.query.search || ''; + const tab = req.query.tab || ''; + + const dataType = !isEmpty(tab) && typeof tab === 'string' ? constantsUtil.searchDataTypes[tab] : ''; + + let baseQuery = { $and: [{ activeflag: 'active' }] }; + + if (dataType === 'collection') { + baseQuery['$and'].push({ publicflag: true }); + } + + if (dataType === 'course') { + baseQuery['$and'].push({ + $or: [{ 'courseOptions.startDate': { $gte: new Date(Date.now()) } }, { 'courseOptions.flexibleDates': true }], + }); + } + + if (query.length > 0) { + baseQuery['$and'].push({ $text: { $search: query } }); + } + + try { + const filterQuery = getObjectFilters(baseQuery, req.query, dataType); + const useCache = isEqual(baseQuery, filterQuery) && query.length === 0; + + let filters = await this.filtersService.buildFilters(dataType, filterQuery, useCache); + + if (dataType === 'dataset') { + // enable new filter behaviour for datasets only - if statement can be removed to apply to all + filters = await this._modifySelectedFilters(req.query, filters, baseQuery, dataType); + } + + filters['spatialv2'] = searchUtil.arrayToTree(filters['spatial'] || []); + + return res.status(200).json({ + success: true, + filters, + }); + } catch (err) { + process.stdout.write(`${err.message}\n`); + return res.json({ + success: false, + message: err.message, + }); + } + }; + + /** Update options for selected filters by removing themselves from the query. */ + _modifySelectedFilters = async (selected, filters, baseQuery, dataType) => { + const mapperOptions = { + dataset: datasetFilters, + tool: toolFilters, + project: projectFilters, + paper: paperFilters, + collection: collectionFilters, + course: courseFilters, + dataUseRegister: dataUseRegisterFilters, + }; + + await Promise.all( + Object.keys(selected).map(async filter => { + const { key } = (await findNodeInTree(mapperOptions[dataType], filter)) || ''; + + if (key) { + let queryParams = cloneDeep(selected); + + delete queryParams[filter]; + + const filterQuery = await getObjectFilters(baseQuery, queryParams, dataType); + const updatedFilters = await this.filtersService.buildFilters(dataType, filterQuery, false); + + filters[key] = updatedFilters[key]; + } + }) + ); + + return filters; + }; +} diff --git a/src/resources/search/filter.route.js b/src/resources/search/searchFilter.route.js similarity index 62% rename from src/resources/search/filter.route.js rename to src/resources/search/searchFilter.route.js index ec526a9b..ff9946f7 100644 --- a/src/resources/search/filter.route.js +++ b/src/resources/search/searchFilter.route.js @@ -1,58 +1,29 @@ import express from 'express'; + import { getObjectFilters, getFilter } from './search.repository'; +import SearchFilterController from './searchFilter.controller'; import { filtersService } from '../filters/dependency'; -import { isEqual, isEmpty } from 'lodash'; -import searchUtil from './util/search.util'; const router = express.Router(); -const typeMapper = { - Datasets: 'dataset', - Tools: 'tool', - Projects: 'project', - Papers: 'paper', - People: 'person', - Courses: 'course', - Collections: 'collection', - Datauses: 'dataUseRegister', -}; +const searchFilterController = new SearchFilterController(filtersService); // @route GET api/v1/search/filter // @desc GET Get filters // @access Public -router.get('/', async (req, res) => { - let searchString = req.query.search || ''; //If blank then return all - let tab = req.query.tab || ''; //If blank then return all - - const type = !isEmpty(tab) && typeof tab === 'string' ? typeMapper[`${tab}`] : ''; - - let defaultQuery = { $and: [{ activeflag: 'active' }] }; - if (type === 'collection') { - defaultQuery['$and'].push({ publicflag: true }); - } else if (type === 'course') { - defaultQuery['$and'].push({ - $or: [{ 'courseOptions.startDate': { $gte: new Date(Date.now()) } }, { 'courseOptions.flexibleDates': true }], - }); - } - - if (searchString.length > 0) defaultQuery['$and'].push({ $text: { $search: searchString } }); - const filterQuery = getObjectFilters(defaultQuery, req, type); - const useCachedFilters = isEqual(defaultQuery, filterQuery) && searchString.length === 0; - - const filters = await filtersService.buildFilters(type, filterQuery, useCachedFilters); - const spatialV2 = searchUtil.arrayToTree(filters['spatial'] || []); - filters['spatialv2'] = spatialV2; - return res.json({ - success: true, - filters, - }); -}); +router.get('/', searchFilterController.getSearchFilters); // @route GET api/v1/search/filter/topic/:type // @desc GET Get list of topics by entity type // @access Public router.get('/topic/:type', async (req, res) => { - await getFilter('', req.params.type, 'tags.topics', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter( + '', + req.params.type, + 'tags.topics', + true, + getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type) + ) .then(data => { return res.json({ success: true, data }); }) @@ -65,7 +36,13 @@ router.get('/topic/:type', async (req, res) => { // @desc GET Get list of features by entity type // @access Public router.get('/feature/:type', async (req, res) => { - await getFilter('', req.params.type, 'tags.features', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter( + '', + req.params.type, + 'tags.features', + true, + getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type) + ) .then(data => { return res.json({ success: true, data }); }) @@ -83,7 +60,7 @@ router.get('/language/:type', async (req, res) => { req.params.type, 'programmingLanguage.programmingLanguage', true, - getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type) + getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type) ) .then(data => { return res.json({ success: true, data }); @@ -102,7 +79,7 @@ router.get('/category/:type', async (req, res) => { req.params.type, 'categories.category', false, - getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type) + getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type) ) .then(data => { return res.json({ success: true, data }); @@ -116,7 +93,7 @@ router.get('/category/:type', async (req, res) => { // @desc GET Get list of licenses by entity type // @access Public router.get('/license/:type', async (req, res) => { - await getFilter('', req.params.type, 'license', false, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter('', req.params.type, 'license', false, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type)) .then(data => { return res.json({ success: true, data }); }) @@ -129,7 +106,13 @@ router.get('/license/:type', async (req, res) => { // @desc GET Get list of organisations by entity type // @access Public router.get('/organisation/:type', async (req, res) => { - await getFilter('', req.params.type, 'organisation', false, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter( + '', + req.params.type, + 'organisation', + false, + getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type) + ) .then(data => { return res.json({ success: true, data }); }) @@ -142,7 +125,7 @@ router.get('/organisation/:type', async (req, res) => { // @desc GET Get list of features by entity type // @access Public router.get('/domains/:type', async (req, res) => { - await getFilter('', req.params.type, 'domains', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter('', req.params.type, 'domains', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type)) .then(data => { return res.json({ success: true, data }); }) @@ -155,7 +138,7 @@ router.get('/domains/:type', async (req, res) => { // @desc GET Get list of features by entity type // @access Public router.get('/keywords/:type', async (req, res) => { - await getFilter('', req.params.type, 'keywords', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter('', req.params.type, 'keywords', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type)) .then(data => { return res.json({ success: true, data }); }) @@ -168,7 +151,7 @@ router.get('/keywords/:type', async (req, res) => { // @desc GET Get list of features by entity type // @access Public router.get('/awards/:type', async (req, res) => { - await getFilter('', req.params.type, 'award', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req, req.params.type)) + await getFilter('', req.params.type, 'award', true, getObjectFilters({ $and: [{ activeflag: 'active' }] }, req.query, req.params.type)) .then(data => { return res.json({ success: true, data }); }) diff --git a/src/resources/utilities/constants.util.js b/src/resources/utilities/constants.util.js index 0b775d52..956ac338 100644 --- a/src/resources/utilities/constants.util.js +++ b/src/resources/utilities/constants.util.js @@ -334,6 +334,17 @@ const _datasetSortDirections = { desc: -1, }; +const _searchDataTypes = { + Datasets: 'dataset', + Tools: 'tool', + Projects: 'project', + Papers: 'paper', + People: 'person', + Courses: 'course', + Collections: 'collection', + Datauses: 'dataUseRegister', +}; + export default { userTypes: _userTypes, enquiryFormId: _enquiryFormId, @@ -368,4 +379,5 @@ export default { datasetSortDirections: _datasetSortDirections, dataUseRegisterStatus: _dataUseRegisterStatus, dataUseRegisterNotifications: _dataUseRegisterNotifications, + searchDataTypes: _searchDataTypes, }; From 89d0e88affd649e3624e2b5cb0409d20de303fdf Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 15 Jun 2022 15:23:35 +0100 Subject: [PATCH 112/389] tests for updated search filter behaviour --- .../__tests__/searchFilter.controller.test.js | 103 ++++++++++++++++++ .../search/searchFilter.controller.js | 2 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/resources/search/__tests__/searchFilter.controller.test.js diff --git a/src/resources/search/__tests__/searchFilter.controller.test.js b/src/resources/search/__tests__/searchFilter.controller.test.js new file mode 100644 index 00000000..39968c70 --- /dev/null +++ b/src/resources/search/__tests__/searchFilter.controller.test.js @@ -0,0 +1,103 @@ +import sinon from 'sinon'; + +import { filtersService } from '../../filters/dependency'; +import SearchFilterController from '../searchFilter.controller'; + +afterEach(function () { + sinon.restore(); +}); + +describe('searchFilterController', () => { + const mockedRequest = () => { + const req = { + query: {}, + }; + return req; + }; + + const mockedResponse = () => { + const res = {}; + res.status = jest.fn().mockReturnValue(res); + res.json = jest.fn().mockReturnValue(res); + return res; + }; + + const searchFilterController = new SearchFilterController(filtersService); + + describe('getSearchFilters', () => { + it('should return a base list of filters if no filter selected', async () => { + const filterServiceStub = sinon.stub(filtersService, 'buildFilters').resolves({ publisher: ['SAIL', 'PUBLIC HEALTH SCOTLAND'] }); + 3; + let req = mockedRequest(); + let res = mockedResponse(); + + req.query.tab = 'Datasets'; + req.query.search = ''; + + const expectedResponse = { + success: true, + filters: { + publisher: ['SAIL', 'PUBLIC HEALTH SCOTLAND'], + spatialv2: [], + }, + }; + + await searchFilterController.getSearchFilters(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(filterServiceStub.calledOnce).toBe(true); + }); + + it('should return a modified list of filters if a given filter group is selected', async () => { + // For example, submitting a request with "datasetpublisher" filter selected + const filterServiceStub = sinon.stub(filtersService, 'buildFilters'); + filterServiceStub.onFirstCall().resolves({ publisher: ['SAIL', 'PUBLIC HEALTH SCOTLAND'] }); + filterServiceStub.onSecondCall().resolves({ publisher: ['SAIL', 'PUBLIC HEALTH SCOTLAND', 'BARTS'] }); + + let req = mockedRequest(); + let res = mockedResponse(); + + req.query.tab = 'Datasets'; + req.query.search = ''; + req.query.datasetpublisher = ''; + + const expectedResponse = { + success: true, + filters: { + publisher: ['SAIL', 'PUBLIC HEALTH SCOTLAND', 'BARTS'], + spatialv2: [], + }, + }; + + await searchFilterController.getSearchFilters(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(filterServiceStub.calledTwice).toBe(true); + }); + + it('should return 500 if an exception is thrown', async () => { + // For example, submitting a request with "datasetpublisher" filter selected + const err = 'TEST MESSAGE: error thrown'; + const filterServiceStub = sinon.stub(filtersService, 'buildFilters').rejects(new Error(err)); + + let req = mockedRequest(); + let res = mockedResponse(); + + req.query.tab = 'Datasets'; + req.query.search = ''; + + const expectedResponse = { + success: false, + message: err, + }; + + await searchFilterController.getSearchFilters(req, res); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith(expectedResponse); + expect(filterServiceStub.calledOnce).toBe(true); + }); + }); +}); diff --git a/src/resources/search/searchFilter.controller.js b/src/resources/search/searchFilter.controller.js index 9105bd8e..4dc8564c 100644 --- a/src/resources/search/searchFilter.controller.js +++ b/src/resources/search/searchFilter.controller.js @@ -61,7 +61,7 @@ export default class SearchFilterController { }); } catch (err) { process.stdout.write(`${err.message}\n`); - return res.json({ + return res.status(500).json({ success: false, message: err.message, }); From a4b1802edbb75a835703d78b5bb9aa2775a5c712 Mon Sep 17 00:00:00 2001 From: kandaj Date: Wed, 15 Jun 2022 15:26:03 +0100 Subject: [PATCH 113/389] console.log removed --- src/resources/utilities/emailGenerator.util.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 2b89a481..14fdf155 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -667,7 +667,6 @@ const _displayDataUseRegisterDashboardLink = () => { const _generateDARStatusChangedEmail = options => { let { id, applicationStatus, applicationStatusDesc, projectId, projectName, publisher, datasetTitles, dateSubmitted, applicants } = options; - console.log(applicationStatus); let body = `
Date: Wed, 15 Jun 2022 15:43:16 +0100 Subject: [PATCH 114/389] add filterService private variable --- src/resources/search/searchFilter.controller.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/resources/search/searchFilter.controller.js b/src/resources/search/searchFilter.controller.js index 4dc8564c..3f36c0b3 100644 --- a/src/resources/search/searchFilter.controller.js +++ b/src/resources/search/searchFilter.controller.js @@ -15,8 +15,10 @@ import { import { findNodeInTree } from '../filters/utils/filters.util'; export default class SearchFilterController { + _filtersService; + constructor(filtersService) { - this.filtersService = filtersService; + this._filtersService = filtersService; } /** Get the relevant filters for a given query */ @@ -46,7 +48,7 @@ export default class SearchFilterController { const filterQuery = getObjectFilters(baseQuery, req.query, dataType); const useCache = isEqual(baseQuery, filterQuery) && query.length === 0; - let filters = await this.filtersService.buildFilters(dataType, filterQuery, useCache); + let filters = await this._filtersService.buildFilters(dataType, filterQuery, useCache); if (dataType === 'dataset') { // enable new filter behaviour for datasets only - if statement can be removed to apply to all @@ -90,7 +92,7 @@ export default class SearchFilterController { delete queryParams[filter]; const filterQuery = await getObjectFilters(baseQuery, queryParams, dataType); - const updatedFilters = await this.filtersService.buildFilters(dataType, filterQuery, false); + const updatedFilters = await this._filtersService.buildFilters(dataType, filterQuery, false); filters[key] = updatedFilters[key]; } From 2a37fa96757e4c1e0826452e3a4fb81e17b371b0 Mon Sep 17 00:00:00 2001 From: kandaj Date: Thu, 16 Jun 2022 11:35:13 +0100 Subject: [PATCH 115/389] workflow action email template updates --- src/resources/utilities/emailGenerator.util.js | 10 +++++----- src/resources/workflow/workflow.service.js | 12 +++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 14fdf155..03d9505a 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -1232,8 +1232,8 @@ const _generateNewReviewPhaseEmail = options => { return body; }; -const _generateWorkflowCreated = options => { - let { workflowName, steps, createdAt, actioner } = options; +const _generateWorkflowActionEmail = options => { + let { workflowName, steps, createdAt, actioner, action } = options; let table = `
{ @@ -2726,7 +2726,7 @@ export default { generateWordContent: _generateWordContent, //Workflows generateWorkflowAssigned: _generateWorkflowAssigned, - generateWorkflowCreated: _generateWorkflowCreated, + generateWorkflowActionEmail: _generateWorkflowActionEmail, //Metadata Onboarding generateMetadataOnboardingSumbitted: _generateMetadataOnboardingSumbitted, generateMetadataOnboardingApproved: _generateMetadataOnboardingApproved, diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index c0b4a6ef..87b7c34d 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -165,17 +165,19 @@ export default class WorkflowService { custodianManagers = teamController.getTeamMembersByRole(publisherObj, 'All'); if (publisherObj.notifications[0].optIn) { publisherObj.notifications[0].subscribedEmails.map(teamEmail => { - custodianManagers.push({email: teamEmail}); + custodianManagers.push({ email: teamEmail }); }); } managerUserIds = custodianManagers.map(user => user.id); let { workflowName = 'Workflow Title', _id, steps, createdAt } = workflow; + const action = type.replace('Workflow', '').toLowerCase(); options = { actioner, workflowName, _id, steps, createdAt, + action, }; // switch over types @@ -190,7 +192,7 @@ export default class WorkflowService { _id ); // 5. Generate the email - html = await emailGenerator.generateWorkflowCreated(options); + html = await emailGenerator.generateWorkflowActionEmail(options); // 6. Send email to custodian managers only within the team await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been created`, html, false); break; @@ -205,7 +207,7 @@ export default class WorkflowService { _id ); // 5. Generate the email - html = await emailGenerator.generateWorkflowCreated(options); + html = await emailGenerator.generateWorkflowActionEmail(options); // 6. Send email to custodian managers only within the team await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been updated`, html, false); break; @@ -220,11 +222,11 @@ export default class WorkflowService { _id ); // 5. Generate the email - html = await emailGenerator.generateWorkflowCreated(options); + html = await emailGenerator.generateWorkflowActionEmail(options); // 6. Send email to custodian managers only within the team await emailGenerator.sendEmail(custodianManagers, constants.hdrukEmail, `A Workflow has been deleted`, html, false); break; - } + } } } From 95c5ac8e67447f516c3c64afa2c955f3c2820746 Mon Sep 17 00:00:00 2001 From: kandaj Date: Thu, 16 Jun 2022 11:44:46 +0100 Subject: [PATCH 116/389] template text updated --- src/resources/utilities/emailGenerator.util.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 14fdf155..02e9614a 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -684,8 +684,7 @@ const _generateDARStatusChangedEmail = options => { From 1ffd0bf81e2343d3747e83f1619e31575c34f4c6 Mon Sep 17 00:00:00 2001 From: kandaj Date: Fri, 17 Jun 2022 09:29:59 +0100 Subject: [PATCH 117/389] workflow email template updates --- src/resources/utilities/emailGenerator.util.js | 9 ++++++--- src/resources/workflow/workflow.service.js | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 03d9505a..7f782a85 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -1233,7 +1233,8 @@ const _generateNewReviewPhaseEmail = options => { }; const _generateWorkflowActionEmail = options => { - let { workflowName, steps, createdAt, actioner, action } = options; + let { workflowName, steps, actioner, action } = options; + const currentDate = new Date().toISOString(); let table = `
- A new Workflow has been created. + A new Workflow has been ${action}.
- ${actioner} has created ${workflowName} on ${moment(createdAt).format('D MMM YYYY')} + ${actioner} has ${action} ${workflowName} on ${moment(createdAt).format('D MMM YYYY')}
Your data access request for ${projectName || datasetTitles} has been ${applicationStatus} by ${publisher}. - Summary information about your approved project will be included in the Gateway data use register. - You will be notified as soon as this becomes visible and searchable on the Gateway. + See below for more details or contact the data custodian.
{ diff --git a/src/resources/workflow/workflow.service.js b/src/resources/workflow/workflow.service.js index 87b7c34d..3fc4120c 100644 --- a/src/resources/workflow/workflow.service.js +++ b/src/resources/workflow/workflow.service.js @@ -202,7 +202,7 @@ export default class WorkflowService { // 4. Create notifications for the managers only await notificationBuilder.triggerNotificationMessage( managerUserIds, - `A new workflow of ${workflowName} has been updated`, + `A workflow of ${workflowName} has been updated`, 'workflow', _id ); @@ -217,7 +217,7 @@ export default class WorkflowService { // 4. Create notifications for the managers only await notificationBuilder.triggerNotificationMessage( managerUserIds, - `A new workflow of ${workflowName} has been deleted`, + `A workflow of ${workflowName} has been deleted`, 'workflow', _id ); From bd1a1918e58f889669db652176065bb355ed68ba Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Fri, 17 Jun 2022 13:45:21 +0100 Subject: [PATCH 118/389] bug fix email notification --- .../datarequest/datarequest.controller.js | 70 ++++++++++++------- src/resources/message/message.controller.js | 2 +- src/resources/team/team.controller.js | 4 +- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 010ed4c4..0bd20b65 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -453,7 +453,7 @@ export default class DataRequestController extends Controller { } // publish the message to GCP PubSub - const cacheEnabled = process.env.CACHE_ENABLED || false; + const cacheEnabled = parseInt(process.env.CACHE_ENABLED) || 0; if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); @@ -2015,12 +2015,15 @@ export default class DataRequestController extends Controller { questionWithAnswer = {}, } = context; + let notificationTeam = []; + switch (type) { case constants.notificationTypes.INPROGRESS: custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); custodianManagersIds = custodianManagers.map(user => user.id); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2067,8 +2070,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2133,8 +2137,9 @@ export default class DataRequestController extends Controller { accessRecord._id, accessRecord.datasets[0].publisher._id.toString() ); - if (accessRecord.datasets[0].publisher.team.notifications[0].optIn) { - accessRecord.datasets[0].publisher.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.datasets[0].publisher.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2150,8 +2155,9 @@ export default class DataRequestController extends Controller { accessRecord._id, accessRecord.datasets[0].publisher._id.toString() ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2254,14 +2260,16 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.datasets[0].publisher.team.notifications[0].optIn) { - accessRecord.datasets[0].publisher.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.datasets[0].publisher.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } } else if (_.has(accessRecord, 'publisherObj') && accessRecord.publisherObj.allowAccessRequestManagement) { // Retrieve all custodian user Ids to generate notifications custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); + custodianUserIds = custodianManagers.map(user => user.id); await notificationBuilder.triggerNotificationMessage( custodianUserIds, @@ -2269,8 +2277,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2484,8 +2493,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2513,8 +2523,9 @@ export default class DataRequestController extends Controller { case constants.notificationTypes.DEADLINEWARNING: custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); custodianManagersIds = custodianManagers.map(user => user.id); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2566,8 +2577,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2621,8 +2633,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2750,8 +2763,9 @@ export default class DataRequestController extends Controller { 'data access request', accessRecord._id ); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2835,8 +2849,9 @@ export default class DataRequestController extends Controller { const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); const custodianManagersIds = custodianManagers.map(user => user.id); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } @@ -2869,8 +2884,9 @@ export default class DataRequestController extends Controller { const custodianManagers = teamController.getTeamMembersByRole(accessRecord.publisherObj.team, 'All'); const custodianManagersIds = custodianManagers.map(user => user.id); - if (accessRecord.publisherObj.team.notifications[0].optIn) { - accessRecord.publisherObj.team.notifications[0].subscribedEmails.map(teamEmail => { + notificationTeam = accessRecord.publisherObj.team.notifications; + if (notificationTeam.length && notificationTeam[0].optIn) { + notificationTeam[0].subscribedEmails.map(teamEmail => { custodianManagers.push({email: teamEmail}); }); } diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 3252dba5..1f4b0187 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -187,7 +187,7 @@ module.exports = { } // publish the message to GCP PubSub - const cacheEnabled = process.env.CACHE_ENABLED || false; + const cacheEnabled = parseInt(process.env.CACHE_ENABLED) || 0; if(cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index b046623a..2c4d2444 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -968,12 +968,12 @@ const getTeamMembersByRole = (team, role) => { let { members = [], users = [] } = team; let userIds = members.filter(mem => { - if (mem.roles.includes(role) || role === 'All') { + if (mem.roles.includes(role) || (role === 'All' && _.has(mem, 'roles'))) { if(!_.has(mem, 'notifications')) { return true; } - if (_.has(mem, 'notifications') && !mem.notifications.length) { + if (_.has(mem, 'notifications') && mem.notifications.length === 0) { return true; } From 79198d0cff9e20fd094c7dfc3e72a24e5beef587 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 20 Jun 2022 11:20:53 +0100 Subject: [PATCH 119/389] Initial commit --- .../schema/datarequest.schemas.model.js | 4 ++ .../questionbank/questionbank.service.js | 37 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/resources/datarequest/schema/datarequest.schemas.model.js b/src/resources/datarequest/schema/datarequest.schemas.model.js index b3a65f01..447bb5f1 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.model.js +++ b/src/resources/datarequest/schema/datarequest.schemas.model.js @@ -36,6 +36,10 @@ const DataRequestSchemas = new Schema( type: Object, default: {}, }, + questionSetStatus: { + type: Object, + default: {}, + }, countOfChanges: { type: Number, default: 0, diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 01669d99..ec5aa640 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -16,14 +16,15 @@ export default class QuestionbankService { let dataRequestSchemas = await this.dataRequestRepository.getApplicationFormSchemas(publisher); if (isEmpty(dataRequestSchemas)) { - //create the questionStatus from the master schema let questionStatus = await this.getDefaultQuestionStates(); + let questionSetStatus = await this.getDefaultQuestionSetStates(); const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus, + questionSetStatus, guidance: {}, countOfChanges: 0, unpublishedGuidance: [], @@ -34,6 +35,7 @@ export default class QuestionbankService { return { masterSchema, questionStatus: schema.questionStatus, + questionSetStatus: schema.questionSetStatus, guidance: schema.guidance, countOfChanges: schema.countOfChanges, schemaId: schema._id, @@ -44,6 +46,7 @@ export default class QuestionbankService { const latestSchemaVersion = dataRequestSchemas[0]; if (latestSchemaVersion.status === 'draft') { let newQuestionStatus = latestSchemaVersion.questionStatus; + let newQuestionSetStatus = latestSchemaVersion.questionSetStatus; let newQuestionsAdded = this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, newQuestionStatus); //Add new questions from the master schema if any @@ -53,6 +56,7 @@ export default class QuestionbankService { return { masterSchema, questionStatus: newQuestionStatus, + questionSetStatus: newQuestionSetStatus, guidance: latestSchemaVersion.guidance, countOfChanges: latestSchemaVersion.countOfChanges, schemaId: latestSchemaVersion._id, @@ -63,15 +67,18 @@ export default class QuestionbankService { if (latestSchemaVersion.status === 'active') { if (!isEmpty(latestSchemaVersion.questionStatus)) { let newQuestionStatus = latestSchemaVersion.questionStatus; + let newQuestionSetStatus = latestSchemaVersion.questionSetStatus; //Add new questions from the master schema if any this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, newQuestionStatus); + this.addQuestionSetsFromMasterSchema(masterSchema, latestSchemaVersion, newQuestionSetStatus); const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus: newQuestionStatus, + questionSetStatus: newQuestionSetStatus, guidance: latestSchemaVersion.guidance, version: latestSchemaVersion.version + 1, countOfChanges: 0, @@ -83,6 +90,7 @@ export default class QuestionbankService { return { masterSchema, questionStatus: newSchema.questionStatus, + questionSetStatus: newSchema.questionSetStatus, guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, schemaId: schema._id, @@ -90,18 +98,21 @@ export default class QuestionbankService { }; } else { let questionStatus = {}; + let questionSetStatus = {}; //Add questions from the publisher schema this.addQuestionsFromPublisherSchema(latestSchemaVersion, questionStatus); //Add question from master schema if not in the publisher schema this.addQuestionsFromMasterSchema(masterSchema, latestSchemaVersion, questionStatus); + this.addQuestionSetsFromMasterSchema(masterSchema, latestSchemaVersion, questionSetStatus); const newSchema = { publisher: publisher.name, status: 'draft', isCloneable: true, questionStatus, + questionSetStatus, guidance: {}, countOfChanges: 0, version: latestSchemaVersion.version + 1, @@ -113,6 +124,7 @@ export default class QuestionbankService { return { masterSchema, questionStatus: newSchema.questionStatus, + questionSetStatus: newSchema.questionSetStatus, guidance: newSchema.guidance, countOfChanges: newSchema.countOfChanges, schemaId: schema._id, @@ -194,6 +206,14 @@ export default class QuestionbankService { return newQuestionsAdded; } + addQuestionSetsFromMasterSchema(masterSchema, publisherSchema, questionSetStatus) { + masterSchema.questionSets.forEach(questionSet => { + if (!has(publisherSchema.questionSetStatus, questionSet.questionSetId)) { + questionSetStatus[questionSet.questionSetId] = 1; + } + }); + } + async revertChanges(publisherId, target) { const publisher = await this.publisherService.getPublisher(publisherId); const dataRequestSchemas = await this.dataRequestRepository.getApplicationFormSchemas(publisher); @@ -260,6 +280,21 @@ export default class QuestionbankService { return defaultQuestionStates; } + async getDefaultQuestionSetStates() { + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const masterSchema = global.masterSchema; + + let defaultQuestionSetStates = {}; + + masterSchema.questionSets.forEach(questionSet => { + if (!has(publisherSchema.questionSetStatus, questionSet.questionSetId)) { + defaultQuestionSetStates[questionSet.questionSetId] = 1; + } + }); + + return defaultQuestionStates; + } + async getPanelQuestions(target) { const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); const questionSets = global.masterSchema.questionSets; From 899de6e55d4ba8e8027912c0021305852286a779 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 20 Jun 2022 11:48:22 +0100 Subject: [PATCH 120/389] Add question set to output schema --- src/resources/datarequest/datarequest.controller.js | 3 +++ src/resources/datarequest/datarequest.model.js | 4 ++++ src/resources/datarequest/datarequest.service.js | 7 +++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 20e00e5f..209ae19c 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -256,6 +256,7 @@ export default class DataRequestController extends Controller { // 5. If in progress application found use existing endpoint to handle logic to fetch and return if (accessRecord) { + console.log('Exists'); req.params.id = accessRecord._id; return await this.getAccessRequestById(req, res); } else { @@ -266,6 +267,8 @@ export default class DataRequestController extends Controller { datasetfields: { publisher = '' }, } = datasets[0]; + console.log('Building'); + // 1. GET the template from the custodian or take the default (Cannot have dataset specific question sets for multiple datasets) accessRecord = await this.dataRequestService.buildApplicationForm( publisher, diff --git a/src/resources/datarequest/datarequest.model.js b/src/resources/datarequest/datarequest.model.js index 9ce1c8b0..6a82bd2f 100644 --- a/src/resources/datarequest/datarequest.model.js +++ b/src/resources/datarequest/datarequest.model.js @@ -45,6 +45,10 @@ const DataRequestSchema = new Schema( type: Object, default: {}, }, + questionSetStatus: { + type: Object, + default: {}, + }, initialQuestionAnswers: { type: Object, default: {}, diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 6dd2c8c3..026c2dda 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -102,8 +102,10 @@ export default class DataRequestService { // 2. Get schema to base application form on const dataRequestSchema = await this.dataRequestRepository.getApplicationFormSchema(publisher); + console.log('dataRequestSchema', dataRequestSchema); + // 3. Build up the accessModel for the user - const { jsonSchema, _id: schemaId, isCloneable = false } = dataRequestSchema; + const { jsonSchema, _id: schemaId, questionSetStatus, isCloneable = false } = dataRequestSchema; // 4. Set form type const formType = schemaId.toString === constants.enquiryFormId ? constants.formTypes.Enquiry : constants.formTypes.Extended5Safe; @@ -126,6 +128,7 @@ export default class DataRequestService { applicationStatus: constants.applicationStatuses.INPROGRESS, formType, presubmissionTopic, + questionSetStatus, }; } @@ -580,7 +583,7 @@ export default class DataRequestService { } darContributorsInfo.map(contributorInfo => { - if (isUndefined(contributorInfo.user)) { + if (isUndefined(contributorInfo.user)) { helper.hidePrivateProfileDetails([contributorInfo]); } }); From e7820180c5042fef0a2d59d646d3d68271de560a Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Tue, 21 Jun 2022 12:23:37 +0100 Subject: [PATCH 121/389] added params for Retry Policy --- .../datarequest/datarequest.controller.js | 7 +++--- src/resources/message/message.controller.js | 7 +++--- src/services/google/PubSubWithRetryService.js | 22 +++++++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 0bd20b65..257bda60 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -466,11 +466,12 @@ export default class DataRequestController extends Controller { id: accessRecord.publisherObj._id, name: accessRecord.publisherObj.name, }, - data: { + details: { dataRequestId: accessRecord._id, createdDate: accessRecord.createdAt, - data: accessRecord.questionAnswers, - } + questionBank: accessRecord.questionAnswers, + }, + darIntegration: publisherDetails['dar-integration'], }; await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 1f4b0187..4bfee28a 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -199,13 +199,14 @@ module.exports = { id: publisherDetails._id, name: publisherDetails.name, }, - data: { + details: { topicId: topicObj._id, messageId: message.messageID, createdDate: message.createdDate, - data: req.body.firstMessage, + questionBank: req.body.firstMessage, - } + }, + darIntegration: publisherDetails['dar-integration'], }; await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } diff --git a/src/services/google/PubSubWithRetryService.js b/src/services/google/PubSubWithRetryService.js index 2bc7822f..da06ddc7 100644 --- a/src/services/google/PubSubWithRetryService.js +++ b/src/services/google/PubSubWithRetryService.js @@ -27,6 +27,14 @@ export const publishMessageWithRetryToPubSub = async (topicName, message) => { messages, }; + const initialRetryDelayMillis = parseInt(process.env.PUBSUB_RP_INITIAL_RETRY_DELAY) || 100; + const retryDelayMultiplier = parseFloat(process.env.PUBSUB_RP_RETRY_DELAY_MULTIPLIER) || 2.0; + const maxRetryDelayMillis = parseInt(process.env.PUBSUB_RP_MAX_RETRY_DELAY) || 60000; + const initialRpcTimeoutMillis = parseInt(process.env.PUBSUB_RP_INITIAL_RPC_TIMEOUT) || 5000; + const rpcTimeoutMultiplier = parseFloat(process.env.PUBSUB_RP_RPC_TIMEOUT_MULTIPLIER) || 2.0; + const maxRpcTimeoutMillis = parseInt(process.env.PUBSUB_RP_MAX_RPC_TIMEOUT) || 600000; + const totalTimeoutMillis = parseInt(process.env.PUBSUB_RP_TOTAL_TIMEOUT) || 600000; + // Retry settings control how the publisher handles retryable failures. Default values are shown. // The `retryCodes` array determines which grpc errors will trigger an automatic retry. // The `backoffSettings` object lets you specify the behaviour of retries over time. @@ -43,23 +51,23 @@ export const publishMessageWithRetryToPubSub = async (topicName, message) => { backoffSettings: { // The initial delay time, in milliseconds, between the completion // of the first failed request and the initiation of the first retrying request. - initialRetryDelayMillis: 100, + initialRetryDelayMillis, // The multiplier by which to increase the delay time between the completion // of failed requests, and the initiation of the subsequent retrying request. - retryDelayMultiplier: 1.3, + retryDelayMultiplier, // The maximum delay time, in milliseconds, between requests. // When this value is reached, retryDelayMultiplier will no longer be used to increase delay time. - maxRetryDelayMillis: 60000, + maxRetryDelayMillis, // The initial timeout parameter to the request. - initialRpcTimeoutMillis: 5000, + initialRpcTimeoutMillis, // The multiplier by which to increase the timeout parameter between failed requests. - rpcTimeoutMultiplier: 1.0, + rpcTimeoutMultiplier, // The maximum timeout parameter, in milliseconds, for a request. When this value is reached, // rpcTimeoutMultiplier will no longer be used to increase the timeout. - maxRpcTimeoutMillis: 600000, + maxRpcTimeoutMillis, // The total time, in milliseconds, starting from when the initial request is sent, // after which an error will be returned, regardless of the retrying attempts made meanwhile. - totalTimeoutMillis: 600000, + totalTimeoutMillis, }, }; From 38cac08c26723b2b6bfb18a48dd04b138a213c31 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Thu, 23 Jun 2022 14:14:00 +0100 Subject: [PATCH 122/389] Clear question set status --- .../questionbank/questionbank.service.js | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index ec5aa640..56d7b6b8 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -1,3 +1,4 @@ +import { Console } from '@sentry/node/dist/integrations'; import { isEmpty, has } from 'lodash'; export default class QuestionbankService { @@ -223,13 +224,15 @@ export default class QuestionbankService { } // Default previous state is the master schema - let previousState = await this.getDefaultQuestionStates(); - + let previousQuestionStatus = await this.getDefaultQuestionStates(); + let previousQuestionSetStatus = await this.getDefaultQuestionSetStates(); let guidance = {}; let unpublishedGuidance = []; + // Is previous version exists, previousState is last schema version if (dataRequestSchemas.length > 1) { - previousState = dataRequestSchemas[1].questionStatus; + previousQuestionStatus = dataRequestSchemas[1].questionStatus; + previousQuestionSetStatus = dataRequestSchemas[1].questionSetStatus; guidance = dataRequestSchemas[1].guidance || {}; } @@ -237,12 +240,19 @@ export default class QuestionbankService { let countOfChanges = 0; if (target) { const panelQuestions = await this.getPanelQuestions(target); - const updates = Object.keys(previousState).filter(key => !panelQuestions.includes(key)); + const updateQuestionStatus = Object.keys(previousQuestionStatus).filter(key => !panelQuestions.includes(key)); + + const global = await this.globalService.getGlobal({ localeId: 'en-gb' }); + const questionSets = global.masterSchema.formPanels.filter(({ pageId }) => pageId !== target); + + questionSets.forEach(({ panelId }) => { + previousQuestionSetStatus[panelId] = dataRequestSchemas[0].questionSetStatus[panelId]; + }); - updates.forEach(key => { - if (previousState[key] !== dataRequestSchemas[0].questionStatus[key]) countOfChanges += 1; + updateQuestionStatus.forEach(key => { + if (previousQuestionStatus[key] !== dataRequestSchemas[0].questionStatus[key]) countOfChanges += 1; - previousState[key] = dataRequestSchemas[0].questionStatus[key]; + previousQuestionStatus[key] = dataRequestSchemas[0].questionStatus[key]; if (dataRequestSchemas[0].unpublishedGuidance.includes(key)) { unpublishedGuidance.push(key); @@ -255,7 +265,8 @@ export default class QuestionbankService { } await this.dataRequestRepository.updateApplicationFormSchemaById(dataRequestSchemas[0]._id, { - questionStatus: previousState, + questionStatus: previousQuestionStatus, + questionSetStatus: previousQuestionSetStatus, unpublishedGuidance, guidance, countOfChanges, @@ -287,12 +298,10 @@ export default class QuestionbankService { let defaultQuestionSetStates = {}; masterSchema.questionSets.forEach(questionSet => { - if (!has(publisherSchema.questionSetStatus, questionSet.questionSetId)) { - defaultQuestionSetStates[questionSet.questionSetId] = 1; - } + defaultQuestionSetStates[questionSet.questionSetId] = 1; }); - return defaultQuestionStates; + return defaultQuestionSetStates; } async getPanelQuestions(target) { From 72793a895d39b196c6cae2c974a6a562531261ea Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Thu, 23 Jun 2022 14:37:00 +0100 Subject: [PATCH 123/389] Question bank tests --- .../activeSchemaCreatedThroughForm.js | 8 +++++++ .../draftSchemaCreatedThroughForm.js | 4 ++++ .../questionbank/__mocks__/questionbank.js | 13 ++++++++++ .../__tests__/questionbank.service.test.js | 24 +++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js index 2ffe4a21..1c4d7e71 100644 --- a/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/activeSchemaCreatedThroughForm.js @@ -7,6 +7,10 @@ export const dataRequestSchema = { countOfChanges: 0, status: 'active', isCloneable: true, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safepeople-otherindividuals': 1, + }, questionStatus: { safedatadataflowdiagramenclosed: 1, safeoutputsoutputsdisseminationplansproposalfindings: 0, @@ -26,6 +30,10 @@ export const dataRequestSchema = { }; export const expectedSchema = { + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safepeople-otherindividuals': 1, + }, questionStatus: { safedatadataflowdiagramenclosed: 1, safeoutputsoutputsdisseminationplansproposalfindings: 0, diff --git a/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js index 00779cec..992141b1 100644 --- a/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js +++ b/src/resources/questionbank/__mocks__/draftSchemaCreatedThroughForm.js @@ -12,6 +12,10 @@ export const dataRequestSchema = { safepeopleprimaryapplicantjobtitle: 'Guidance for safepeopleprimaryapplicantjobtitle', safepeopleprimaryapplicanttelephone: 'Guidance for safepeopleprimaryapplicanttelephone', }, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safepeople-otherindividuals': 0, + }, questionStatus: { safepeopleprimaryapplicantfullname: 0, safepeopleprimaryapplicantjobtitle: 1, diff --git a/src/resources/questionbank/__mocks__/questionbank.js b/src/resources/questionbank/__mocks__/questionbank.js index e9c48506..b9df7b68 100644 --- a/src/resources/questionbank/__mocks__/questionbank.js +++ b/src/resources/questionbank/__mocks__/questionbank.js @@ -199,5 +199,18 @@ export const globalDocument = { ], }, ], + formPanels: [ + { panelId: 'safepeople-primaryapplicant', index: 1, pageId: 'safepeople' }, + { + panelId: 'safepeople-otherindividuals', + pageId: 'safepeople', + index: 2, + }, + { + panelId: 'safeproject-projectdetails', + pageId: 'safeproject', + index: 3, + }, + ], }, }; diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index 155debe3..bbf3c577 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -125,6 +125,10 @@ describe('Question Bank Service', function () { safepeopleotherindividualsjobtitle: 0, safepeopleotherindividualsorganisation: 0, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 0, + 'safepeople-otherindividuals': 0, + }, unpublishedGuidance: ['safepeopleprimaryapplicantfullname'], }, { @@ -138,6 +142,10 @@ describe('Question Bank Service', function () { safepeopleotherindividualsjobtitle: 1, safepeopleotherindividualsorganisation: 1, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safepeople-otherindividuals': 1, + }, unpublishedGuidance: [], }, ]); @@ -154,6 +162,10 @@ describe('Question Bank Service', function () { safepeopleotherindividualsjobtitle: 1, safepeopleotherindividualsorganisation: 1, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safepeople-otherindividuals': 1, + }, unpublishedGuidance: [], guidance: {}, countOfChanges: 0, @@ -177,6 +189,10 @@ describe('Question Bank Service', function () { safeprojectprojectdetailstype: 0, safeprojectprojectdetailsneworexisting: 0, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 0, + 'safeproject-projectdetails': 0, + }, unpublishedGuidance: ['safepeopleprimaryapplicantfullname'], }, { @@ -190,6 +206,10 @@ describe('Question Bank Service', function () { safeprojectprojectdetailstype: 1, safeprojectprojectdetailsneworexisting: 1, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safeproject-projectdetails': 1, + }, unpublishedGuidance: [], }, ]); @@ -206,6 +226,10 @@ describe('Question Bank Service', function () { safeprojectprojectdetailstype: 0, safeprojectprojectdetailsneworexisting: 0, }, + questionSetStatus: { + 'safepeople-primaryapplicant': 1, + 'safeproject-projectdetails': 0, + }, unpublishedGuidance: [], guidance: {}, countOfChanges: 3, From 380f5e907aac0efac6c9d6d47f7e0b399845c90b Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 24 Jun 2022 09:26:20 +0100 Subject: [PATCH 124/389] Remove console logs --- src/resources/datarequest/datarequest.controller.js | 3 --- src/resources/datarequest/datarequest.service.js | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index 209ae19c..20e00e5f 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -256,7 +256,6 @@ export default class DataRequestController extends Controller { // 5. If in progress application found use existing endpoint to handle logic to fetch and return if (accessRecord) { - console.log('Exists'); req.params.id = accessRecord._id; return await this.getAccessRequestById(req, res); } else { @@ -267,8 +266,6 @@ export default class DataRequestController extends Controller { datasetfields: { publisher = '' }, } = datasets[0]; - console.log('Building'); - // 1. GET the template from the custodian or take the default (Cannot have dataset specific question sets for multiple datasets) accessRecord = await this.dataRequestService.buildApplicationForm( publisher, diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 026c2dda..7f69530e 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -102,8 +102,6 @@ export default class DataRequestService { // 2. Get schema to base application form on const dataRequestSchema = await this.dataRequestRepository.getApplicationFormSchema(publisher); - console.log('dataRequestSchema', dataRequestSchema); - // 3. Build up the accessModel for the user const { jsonSchema, _id: schemaId, questionSetStatus, isCloneable = false } = dataRequestSchema; From d7ebd84b1c0a99f86aaef2dd6c27a4a2f04cdde6 Mon Sep 17 00:00:00 2001 From: kandaj Date: Fri, 24 Jun 2022 14:40:30 +0100 Subject: [PATCH 125/389] question bank flag added --- src/resources/publisher/publisher.model.js | 3 +++ src/resources/team/team.controller.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/resources/publisher/publisher.model.js b/src/resources/publisher/publisher.model.js index 97539243..c8775295 100644 --- a/src/resources/publisher/publisher.model.js +++ b/src/resources/publisher/publisher.model.js @@ -41,6 +41,9 @@ const PublisherSchema = new Schema( accessRequestCost: String, dataUseLimitation: [String], dataUseRequirements: [String], + questionBank: { + enabled: { type: Boolean, default: false }, + }, }, mdcFolderId: String, rorOrgId: String, diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 531a12c1..da714fe1 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -578,7 +578,7 @@ const getTeamsList = async (req, res) => { membersCount: { $size: '$members' }, } ) - .populate('publisher', { name: 1, 'publisherDetails.name': 1, 'publisherDetails.memberOf': 1 }) + .populate('publisher', { name: 1, 'publisherDetails.name': 1, 'publisherDetails.memberOf': 1, 'questionBank.enabled': 1 }) .populate('users', { firstname: 1, lastname: 1 }) .sort({ updatedAt: -1 }) .lean(); @@ -1052,7 +1052,7 @@ const filterMembersByNoticationTypesOptIn = (members, notificationTypes) => { } return some(member.notifications, notification => { - return includes(notificationTypes, notification.notificationType) && (notification.optIn === true); + return includes(notificationTypes, notification.notificationType) && notification.optIn === true; }); }); }; From 9c089bc68a029e8bb69bbd512f0ff43fbfd6887b Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 24 Jun 2022 15:25:50 +0100 Subject: [PATCH 126/389] add questionBankEnabled flag to PUT --- src/resources/team/team.controller.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index da714fe1..468a3395 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -768,7 +768,12 @@ const editTeam = async (req, res) => { } const id = req.params.id; - const { name, memberOf, contactPoint } = req.body; + const { name, memberOf, contactPoint, questionBankEnabled } = req.body; + + if (!(typeof questionBankEnabled === 'boolean')) { + return res.status(400).json({ success: false, message: 'questionBankEnabled must be of boolean type' }); + } + const existingTeamDetails = await PublisherModel.findOne({ _id: ObjectId(id) }).lean(); //3. Update Team @@ -780,6 +785,9 @@ const editTeam = async (req, res) => { name, memberOf, contactPoint, + questionBank: { + enabled: questionBankEnabled, + }, }, }, err => { From 116d3f6db9219fdad2076e3550404966e3bb83b7 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 24 Jun 2022 15:40:13 +0100 Subject: [PATCH 127/389] add questionBankEnabled flag to PUT - UPDATE --- src/resources/team/team.controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 468a3395..2dabda77 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -770,7 +770,7 @@ const editTeam = async (req, res) => { const id = req.params.id; const { name, memberOf, contactPoint, questionBankEnabled } = req.body; - if (!(typeof questionBankEnabled === 'boolean')) { + if (!_.isUndefined(questionBankEnabled) && !(typeof questionBankEnabled === 'boolean')) { return res.status(400).json({ success: false, message: 'questionBankEnabled must be of boolean type' }); } @@ -785,9 +785,9 @@ const editTeam = async (req, res) => { name, memberOf, contactPoint, - questionBank: { - enabled: questionBankEnabled, - }, + ...(!_.isUndefined(questionBankEnabled) && { + 'questionBank.enabled': questionBankEnabled, + }), }, }, err => { From 1c1a3c37d99fda0b5a0bebb5f88f2012d5ab628b Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Tue, 28 Jun 2022 13:14:30 +0100 Subject: [PATCH 128/389] Fixes --- src/resources/questionbank/questionbank.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/questionbank/questionbank.service.js b/src/resources/questionbank/questionbank.service.js index 56d7b6b8..84cf8e40 100644 --- a/src/resources/questionbank/questionbank.service.js +++ b/src/resources/questionbank/questionbank.service.js @@ -298,7 +298,7 @@ export default class QuestionbankService { let defaultQuestionSetStates = {}; masterSchema.questionSets.forEach(questionSet => { - defaultQuestionSetStates[questionSet.questionSetId] = 1; + defaultQuestionSetStates[questionSet.questionSetId] = typeof questionSet.active !== 'undefined' ? questionSet.active : 1; }); return defaultQuestionSetStates; From 91ccbbf8c88643f54c5bc0d92c279520b12b65bc Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Thu, 30 Jun 2022 11:31:03 +0100 Subject: [PATCH 129/389] fix limit on datasetsv2 api --- src/resources/base/repository.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/base/repository.js b/src/resources/base/repository.js index 471c7d56..f7ea7d91 100644 --- a/src/resources/base/repository.js +++ b/src/resources/base/repository.js @@ -48,7 +48,7 @@ export default class Repository { // Pagination const page = query.page * 1 || 1; - const limit = (query["limit"] !== undefined) ? query.limit * 1 : 500; + const limit = query['limit'] !== undefined ? query.limit * 1 : 1500; const skip = (page - 1) * limit; results = results.skip(skip).limit(limit); From 1d99b5585126dc3415acbf512aa53214fd21c38e Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 4 Jul 2022 16:10:17 +0100 Subject: [PATCH 130/389] Update question bank in publisher --- src/resources/publisher/publisher.controller.js | 13 +++++++++++++ src/resources/publisher/publisher.route.js | 7 +++++++ src/resources/publisher/publisher.service.js | 11 +++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index b124b197..a701cc47 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -173,4 +173,17 @@ export default class PublisherController extends Controller { }); } } + + async updateQuestionBank(req, res) { + try { + await this.publisherService.updateQuestionBank(req.params.id, req.body).then(() => { + 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', + }); + } + } } diff --git a/src/resources/publisher/publisher.route.js b/src/resources/publisher/publisher.route.js index 43cbcb99..614e0664 100644 --- a/src/resources/publisher/publisher.route.js +++ b/src/resources/publisher/publisher.route.js @@ -59,4 +59,11 @@ router.patch('/dataRequestModalContent/:id', passport.authenticate('jwt'), utils publisherController.updateDataRequestModalContent(req, res) ); +router.patch( + '/:id/questionbank', + passport.authenticate('jwt'), + logger.logRequestMiddleware({ logCategory, action: 'Updating question bank enabled / disabled' }), + (req, res) => publisherController.updateQuestionBank(req, res) +); + module.exports = router; diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index 00b706dc..a3d7e06c 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -95,10 +95,9 @@ export default class PublisherService { return filteredApplications; } - async update(document, body = {}) { return this.publisherRepository.update(document, body); - } + } async updateDataRequestModalContent(publisherId, requestingUserId, content) { await this.publisherRepository.updatePublisher( @@ -109,6 +108,14 @@ export default class PublisherService { dataRequestModalContent: { header: '', body: content, footer: '' }, } ); + } + async updateQuestionBank(publisherId, data) { + await this.publisherRepository.updatePublisher( + { _id: publisherId }, + { + 'publisherDetails.questionBank': data, + } + ); } } From 4adc8ff88d4d5694212f52e4ab0ac4c9ec77ae68 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Mon, 4 Jul 2022 16:54:39 +0100 Subject: [PATCH 131/389] Fix overriding publisherDetails --- src/resources/team/team.controller.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 2dabda77..a0d0ab0a 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -578,7 +578,12 @@ const getTeamsList = async (req, res) => { membersCount: { $size: '$members' }, } ) - .populate('publisher', { name: 1, 'publisherDetails.name': 1, 'publisherDetails.memberOf': 1, 'questionBank.enabled': 1 }) + .populate('publisher', { + name: 1, + 'publisherDetails.name': 1, + 'publisherDetails.memberOf': 1, + 'publisherDetails.questionBank.enabled': 1, + }) .populate('users', { firstname: 1, lastname: 1 }) .sort({ updatedAt: -1 }) .lean(); @@ -768,11 +773,7 @@ const editTeam = async (req, res) => { } const id = req.params.id; - const { name, memberOf, contactPoint, questionBankEnabled } = req.body; - - if (!_.isUndefined(questionBankEnabled) && !(typeof questionBankEnabled === 'boolean')) { - return res.status(400).json({ success: false, message: 'questionBankEnabled must be of boolean type' }); - } + const { name, memberOf, contactPoint } = req.body; const existingTeamDetails = await PublisherModel.findOne({ _id: ObjectId(id) }).lean(); @@ -781,14 +782,9 @@ const editTeam = async (req, res) => { { _id: ObjectId(id) }, { name: `${memberOf} > ${name}`, - publisherDetails: { - name, - memberOf, - contactPoint, - ...(!_.isUndefined(questionBankEnabled) && { - 'questionBank.enabled': questionBankEnabled, - }), - }, + 'publisherDetails.name': name, + 'publisherDetails.memberOf': memberOf, + 'publisherDetails.contactPoint': contactPoint, }, err => { if (err) { From eaa24d7326ccf96354833cf1e76d0ffc36cbdec7 Mon Sep 17 00:00:00 2001 From: kandaj Date: Tue, 5 Jul 2022 10:05:37 +0100 Subject: [PATCH 132/389] teams publisher details model updated --- src/resources/team/team.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index da714fe1..efe2cdeb 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -578,7 +578,12 @@ const getTeamsList = async (req, res) => { membersCount: { $size: '$members' }, } ) - .populate('publisher', { name: 1, 'publisherDetails.name': 1, 'publisherDetails.memberOf': 1, 'questionBank.enabled': 1 }) + .populate('publisher', { + name: 1, + 'publisherDetails.name': 1, + 'publisherDetails.memberOf': 1, + 'publisherDetails.questionBank.enabled': 1, + }) .populate('users', { firstname: 1, lastname: 1 }) .sort({ updatedAt: -1 }) .lean(); From 393afc35f861dc138903482664489f16bc997319 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Tue, 5 Jul 2022 11:12:19 +0100 Subject: [PATCH 133/389] Created from wrong branch --- src/resources/auth/utils.js | 13 +++++++++++++ src/resources/publisher/publisher.controller.js | 13 +++++++++++++ src/resources/publisher/publisher.model.js | 7 +++++++ src/resources/publisher/publisher.route.js | 8 ++++++++ src/resources/publisher/publisher.service.js | 14 ++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/src/resources/auth/utils.js b/src/resources/auth/utils.js index 97cb301b..fc181ac7 100644 --- a/src/resources/auth/utils.js +++ b/src/resources/auth/utils.js @@ -202,6 +202,18 @@ 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)) return next(); + + return res.status(401).json({ + status: 'error', + message: 'Unauthorised to perform this action.', + }); +}; + export { setup, signToken, @@ -213,4 +225,5 @@ export { getTeams, catchLoginErrorAndRedirect, loginAndSignToken, + userIsTeamManager, }; diff --git a/src/resources/publisher/publisher.controller.js b/src/resources/publisher/publisher.controller.js index 1cb25865..ca8d4eec 100644 --- a/src/resources/publisher/publisher.controller.js +++ b/src/resources/publisher/publisher.controller.js @@ -161,4 +161,17 @@ export default class PublisherController extends Controller { }); } } + + async updateDataUseWidget(req, res) { + try { + await this.publisherService.updateDataUseWidget(req.params.id, req.body).then(() => { + 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', + }); + } + } } diff --git a/src/resources/publisher/publisher.model.js b/src/resources/publisher/publisher.model.js index 4dbe68c7..81bc6323 100644 --- a/src/resources/publisher/publisher.model.js +++ b/src/resources/publisher/publisher.model.js @@ -37,6 +37,13 @@ const PublisherSchema = new Schema( accessRequestCost: String, dataUseLimitation: [String], dataUseRequirements: [String], + dataUse: { + widget: { + accepted: { type: Boolean, default: false }, + acceptedByUserId: String, + acceptedDate: Date, + }, + }, }, mdcFolderId: String, rorOrgId: String, diff --git a/src/resources/publisher/publisher.route.js b/src/resources/publisher/publisher.route.js index e8a1e392..bf55f8b9 100644 --- a/src/resources/publisher/publisher.route.js +++ b/src/resources/publisher/publisher.route.js @@ -4,6 +4,7 @@ import passport from 'passport'; import { logger } from '../utilities/logger'; import PublisherController from './publisher.controller'; import { publisherService, workflowService, dataRequestService, amendmentService } from './dependency'; +import { userIsTeamManager } from '../auth/utils'; const logCategory = 'Publisher'; const publisherController = new PublisherController(publisherService, workflowService, dataRequestService, amendmentService); @@ -54,4 +55,11 @@ router.get( (req, res) => publisherController.getPublisherWorkflows(req, res) ); +// @route PATCH /api/publishers/:id/dataUseWidget +// @desc Update data use widget settings (terms and conditions) +// @access Public +router.patch('/:id/dataUseWidget', passport.authenticate('jwt'), userIsTeamManager(), (req, res) => + publisherController.updateDataUseWidget(req, res) +); + module.exports = router; diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index f56f5a5c..c2ab0f8d 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -94,4 +94,18 @@ export default class PublisherService { return filteredApplications; } + + async updateDataUseWidget(publisherId, content) { + await this.publisherRepository.updateByQuery( + { _id: publisherId }, + { + 'publisherDetails.dataUse': { + widget: { + ...content, + acceptedDate: Date.now(), + }, + }, + } + ); + } } From e7f59fbc4081a71f1a002e1b44f64af242b96e21 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Mon, 11 Jul 2022 15:14:02 +0100 Subject: [PATCH 134/389] fix questionBank tests --- .../questionbank/__tests__/questionbank.service.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index bbf3c577..5da84b7a 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -13,7 +13,7 @@ import * as noSchemaExists from '../__mocks__/noSchemaExists'; describe('Question Bank Service', function () { const dataRequestRepository = new DataRequestRepository(); const globalService = new GlobalService(); - sinon.stub(globalService, 'getGlobal').returns(questionBank.globalDocument); + sinon.stub(globalService, 'getMasterSchema').returns(questionBank.globalDocument); const publisherRepository = new PublisherRepository(); sinon.stub(publisherRepository, 'getPublisher').returns(questionBank.publisherDocument); const publisherService = new PublisherService(publisherRepository); From d315aee48c48173e2c48504fb01867e5d4afca4c Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Tue, 12 Jul 2022 14:54:20 +0100 Subject: [PATCH 135/389] fix for creating new team --- src/resources/team/team.controller.js | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index f538b5e1..1b56a88b 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -722,7 +722,7 @@ const addTeam = async (req, res) => { // 11. Send email and notification to managers await createNotifications(constants.notificationTypes.TEAMADDED, { recipients }, name, req.user, publisherId); - return res.status(200).json({ success: true }); + return res.status(200).json({ success: true, _id: newTeam._id }); } catch (err) { console.error(err.message); return res.status(500).json({ @@ -971,21 +971,23 @@ const checkIfAdmin = (user, adminRoles) => { const getTeamMembersByRole = (team, role) => { let { members = [], users = [] } = team; - let userIds = members.filter(mem => { - if (mem.roles.includes(role) || (role === 'All' && _.has(mem, 'roles'))) { - if(!_.has(mem, 'notifications')) { - return true; - } + let userIds = members + .filter(mem => { + if (mem.roles.includes(role) || (role === 'All' && _.has(mem, 'roles'))) { + if (!_.has(mem, 'notifications')) { + return true; + } - if (_.has(mem, 'notifications') && mem.notifications.length === 0) { - return true; - } - - if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { - return true; + if (_.has(mem, 'notifications') && mem.notifications.length === 0) { + return true; + } + + if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { + return true; + } } - } - }).map(mem => mem.memberid.toString()); + }) + .map(mem => mem.memberid.toString()); return users.filter(user => userIds.includes(user._id.toString())); }; From 327dc04ebb3e6231350ed7ba25b9b4c9c9829776 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 15 Jul 2022 15:37:07 +0100 Subject: [PATCH 136/389] fix email attachments --- src/resources/utilities/emailGenerator.util.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/resources/utilities/emailGenerator.util.js b/src/resources/utilities/emailGenerator.util.js index 907c58a2..cfd68d19 100644 --- a/src/resources/utilities/emailGenerator.util.js +++ b/src/resources/utilities/emailGenerator.util.js @@ -2668,12 +2668,12 @@ const _generateEmailFooter = (recipient, allowUnsubscribe) => { `; }; -const _generateAttachment = (filename, content, type) => { +const _generateAttachment = (filename, content, contentType) => { return { content, filename, - type, - disposition: 'attachment', + contentType, + encoding: 'base64', }; }; From 9cb0f32922e0213021240465c02fb38da0ed41f9 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Thu, 21 Jul 2022 10:27:04 +0100 Subject: [PATCH 137/389] Handle auth --- .../datarequest/schema/datarequest.schemas.route.js | 4 +++- src/resources/questionbank/questionbank.route.js | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/resources/datarequest/schema/datarequest.schemas.route.js b/src/resources/datarequest/schema/datarequest.schemas.route.js index 93a35542..a1228515 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.route.js +++ b/src/resources/datarequest/schema/datarequest.schemas.route.js @@ -7,6 +7,7 @@ import DatarequestschemaController from './datarequest.schema.controller'; import { datarequestschemaService } from './dependency'; import { utils } from '../../auth'; import { ROLES } from '../../user/user.roles'; +import constants from '../../utilities/constants.util'; const datarequestschemaController = new DatarequestschemaController(datarequestschemaService); @@ -39,8 +40,9 @@ const authorizeUpdate = async (req, res, next) => { } const authorised = isUserMemberOfTeam(requestingUser, datarequestschema.publisher); + const isAdmin = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); - if (!authorised) { + if (!authorised && !isAdmin) { return res.status(401).json({ success: false, message: 'You are not authorised to perform this action', diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index b9531b1d..f8d8e075 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -6,6 +6,7 @@ import { questionbankService } from './dependency'; import { datarequestschemaService } from './../datarequest/schema/dependency'; import { logger } from '../utilities/logger'; import { isUserMemberOfTeamById, isUserMemberOfTeamByName } from '../auth/utils'; +import constants from '../utilities/constants.util'; const router = express.Router(); const questionbankController = new QuestionbankController(questionbankService); @@ -24,8 +25,11 @@ const authorizeViewRequest = (req, res, next) => { const { publisherId } = req.params; const authorised = isUserMemberOfTeamById(requestingUser, publisherId); + const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); - if (!authorised) { + console.log('requestingUser, publisherId', requestingUser, publisherId); + + if (!authorised && !isAdminUser) { return res.status(401).json({ success: false, message: 'You are not authorised to perform this action', @@ -57,8 +61,9 @@ const authorizePostRequest = async (req, res, next) => { } const authorised = isUserMemberOfTeamByName(requestingUser, dataRequestSchema.publisher); + const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); - if (!authorised) { + if (!authorised && !isAdminUser) { return res.status(401).json({ success: false, message: 'You are not authorised to perform this action', @@ -82,7 +87,7 @@ router.get( (req, res) => questionbankController.getQuestionbank(req, res) ); -// @route POST /api/v1/questionbanks +// @route POST /api/v1/questionbank/schemaId // @desc Activate a draft schema creating a jsonSchema from masterSchema // @access Public router.post('/:schemaId', passport.authenticate('jwt'), validatePostRequest, authorizePostRequest, (req, res) => From 3fbb31e436d97f75ed9e520c89a1049c3e96ec87 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Thu, 21 Jul 2022 10:27:31 +0100 Subject: [PATCH 138/389] Remove console log --- src/resources/questionbank/questionbank.route.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/questionbank/questionbank.route.js b/src/resources/questionbank/questionbank.route.js index f8d8e075..54dd3dd9 100644 --- a/src/resources/questionbank/questionbank.route.js +++ b/src/resources/questionbank/questionbank.route.js @@ -27,8 +27,6 @@ const authorizeViewRequest = (req, res, next) => { const authorised = isUserMemberOfTeamById(requestingUser, publisherId); const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); - console.log('requestingUser, publisherId', requestingUser, publisherId); - if (!authorised && !isAdminUser) { return res.status(401).json({ success: false, From 253ce69fd2459760ccc5a58ca6a8792f58b7ac6f Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Thu, 21 Jul 2022 11:15:06 +0100 Subject: [PATCH 139/389] Rename var --- src/resources/datarequest/schema/datarequest.schemas.route.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/datarequest/schema/datarequest.schemas.route.js b/src/resources/datarequest/schema/datarequest.schemas.route.js index a1228515..4cf5698e 100644 --- a/src/resources/datarequest/schema/datarequest.schemas.route.js +++ b/src/resources/datarequest/schema/datarequest.schemas.route.js @@ -40,9 +40,9 @@ const authorizeUpdate = async (req, res, next) => { } const authorised = isUserMemberOfTeam(requestingUser, datarequestschema.publisher); - const isAdmin = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); + const isAdminUser = requestingUser.teams.map(team => team.type).includes(constants.teamTypes.ADMIN); - if (!authorised && !isAdmin) { + if (!authorised && !isAdminUser) { return res.status(401).json({ success: false, message: 'You are not authorised to perform this action', From 989fac4525bfb4c155ff8b12210297ec040531b2 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Thu, 11 Aug 2022 14:48:49 +0100 Subject: [PATCH 140/389] Adding additional props to search results --- src/resources/search/search.repository.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index 99a00ba1..f21e8ad8 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -321,6 +321,9 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, activeflag: 1, counter: 1, 'datasetfields.metadataquality.weighted_quality_score': 1, + 'datasetfields.metadataquality.weighted_quality_rating': 1, + 'datasetfields.metadataquality.weighted_error_percent': 1, + 'datasetfields.metadataquality.weighted_completeness_percent': 1, latestUpdate: '$timestamps.updated', relatedresources: { From d7fbc2432150b138e7428a313e7d0782103eda96 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Mon, 15 Aug 2022 14:14:59 +0100 Subject: [PATCH 141/389] Fix metadata ordering of search results --- .../__tests__/questionbank.service.test.js | 2 +- src/resources/search/search.repository.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/resources/questionbank/__tests__/questionbank.service.test.js b/src/resources/questionbank/__tests__/questionbank.service.test.js index bbf3c577..5da84b7a 100644 --- a/src/resources/questionbank/__tests__/questionbank.service.test.js +++ b/src/resources/questionbank/__tests__/questionbank.service.test.js @@ -13,7 +13,7 @@ import * as noSchemaExists from '../__mocks__/noSchemaExists'; describe('Question Bank Service', function () { const dataRequestRepository = new DataRequestRepository(); const globalService = new GlobalService(); - sinon.stub(globalService, 'getGlobal').returns(questionBank.globalDocument); + sinon.stub(globalService, 'getMasterSchema').returns(questionBank.globalDocument); const publisherRepository = new PublisherRepository(); sinon.stub(publisherRepository, 'getPublisher').returns(questionBank.publisherDocument); const publisherService = new PublisherService(publisherRepository); diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index f21e8ad8..65ff6f20 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -320,7 +320,16 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, activeflag: 1, counter: 1, - 'datasetfields.metadataquality.weighted_quality_score': 1, + + 'datasetfields.metadataquality.weighted_quality_score': { + $convert: { + input: '$datasetfields.metadataquality.weighted_quality_score', + to: 'double', + onError: 0, + onNull: 0, + }, + }, + 'datasetfields.metadataquality.weighted_quality_rating': 1, 'datasetfields.metadataquality.weighted_error_percent': 1, 'datasetfields.metadataquality.weighted_completeness_percent': 1, From 5c50f69f64eef38ced5b42a5d321301ccd584015 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Wed, 17 Aug 2022 14:16:17 +0100 Subject: [PATCH 142/389] Add files object to outgoing DAR application for PubSub --- src/resources/datarequest/datarequest.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index e09b6abc..d9c540b6 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -476,6 +476,7 @@ export default class DataRequestController extends Controller { dataRequestId: accessRecord._id, createdDate: accessRecord.createdAt, questionBank: accessRecord.questionAnswers, + files: accessRecord.files, }, darIntegration: publisherDetails['dar-integration'], }; From 0f18eb672a14399f6e95c93e4a408552d33d7d18 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Mon, 22 Aug 2022 11:03:13 +0100 Subject: [PATCH 143/389] Only send PubSub if not service account --- src/resources/message/message.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 4bfee28a..4ebcbb89 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -19,7 +19,7 @@ module.exports = { // POST /api/v1/messages createMessage: async (req, res) => { try { - const { _id: createdBy, firstname, lastname } = req.user; + const { _id: createdBy, firstname, lastname, isServiceAccount = false } = req.user; let { messageType = 'message', topic = '', messageDescription, relatedObjectIds, firstMessage } = req.body; let topicObj = {}; let team, publisher, userType; @@ -188,7 +188,7 @@ module.exports = { // publish the message to GCP PubSub const cacheEnabled = parseInt(process.env.CACHE_ENABLED) || 0; - if(cacheEnabled) { + if (cacheEnabled && !isServiceAccount) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(tools[0].publisher._id) }).lean(); if (publisherDetails['dar-integration']['enabled']) { From 2e586e160e6dfc66e8245de00d91515db582ff25 Mon Sep 17 00:00:00 2001 From: kandaj Date: Wed, 24 Aug 2022 12:00:27 +0100 Subject: [PATCH 144/389] json files removed --- src/controllers/datasetfiles/61d462527fab6feaf7046bf7.json | 1 - src/controllers/datasetfiles/61d6d831050b530d73689c96.json | 1 - src/controllers/datasetfiles/61fab5b554d6b45d5fa72b58.json | 1 - src/controllers/datasetfiles/61fab8de2f6ba75e6eda6bff.json | 1 - src/controllers/datasetfiles/6231ecea2e022b2e76da14aa.json | 1 - src/controllers/datasetfiles/6239a9eba80b5bb3563911c6.json | 1 - src/controllers/datasetfiles/6257e054b2f9517e2b479157.json | 1 - src/controllers/datasetfiles/dummy.json | 1 - 8 files changed, 8 deletions(-) delete mode 100644 src/controllers/datasetfiles/61d462527fab6feaf7046bf7.json delete mode 100644 src/controllers/datasetfiles/61d6d831050b530d73689c96.json delete mode 100644 src/controllers/datasetfiles/61fab5b554d6b45d5fa72b58.json delete mode 100644 src/controllers/datasetfiles/61fab8de2f6ba75e6eda6bff.json delete mode 100644 src/controllers/datasetfiles/6231ecea2e022b2e76da14aa.json delete mode 100644 src/controllers/datasetfiles/6239a9eba80b5bb3563911c6.json delete mode 100644 src/controllers/datasetfiles/6257e054b2f9517e2b479157.json delete mode 100644 src/controllers/datasetfiles/dummy.json diff --git a/src/controllers/datasetfiles/61d462527fab6feaf7046bf7.json b/src/controllers/datasetfiles/61d462527fab6feaf7046bf7.json deleted file mode 100644 index da4f66ff..00000000 --- a/src/controllers/datasetfiles/61d462527fab6feaf7046bf7.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"OTHER > HEALTH DATA RESEARCH U","description":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations\n\n![](https://raw.githubusercontent.com/HDRUK/papers/master/images/screenshot.png)\n\n","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"OTHER > HEALTH DATA RESEARCH U"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"susheel.varma@hdruk.ac.uk"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Preprints\",\"Papers\",\"HDR UK\"]"},{"namespace":"org.healthdatagateway","key":"properties/summary/doiName","value":"10.1093/ije/dyx196"},{"namespace":"org.healthdatagateway","key":"properties/documentation/description","value":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations\n\n![](https://raw.githubusercontent.com/HDRUK/papers/master/images/screenshot.png)\n\n"},{"namespace":"org.healthdatagateway","key":"properties/documentation/associatedMedia","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/documentation/isPartOf","value":"NOT APPLICABLE"},{"namespace":"org.healthdatagateway","key":"properties/coverage/spatial","value":"[\"https://www.geonames.org/countries/GB/united-kingdom.html\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/typicalAgeRange","value":"0-0"},{"namespace":"org.healthdatagateway","key":"properties/coverage/physicalSampleAvailability","value":"[\"NOT AVAILABLE\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/followup","value":"UNKNOWN"},{"namespace":"org.healthdatagateway","key":"properties/coverage/pathway","value":"NOT APPLICABLE"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/purpose","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/source","value":"[\"MACHINE GENERATED\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/collectionSituation","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"DAILY"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/distributionReleaseDate","value":"2020-11-27"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2020-03-31"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/endDate","value":"2022-04-30"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"NO TIMELAG"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseLimitation","value":"[\"GENERAL RESEARCH USE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseRequirements","value":"[\"RETURN TO DATABASE OR RESOURCE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/resourceCreator","value":"[\"HDR UK Science Team\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/investigations","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/isReferencedBy","value":"[\"https://doi.org/10.5281/zenodo.326615\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"https://raw.githubusercontent.com/HDRUK/papers/master/LICENSE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessService","value":"https://github.com/HDRUK/papers"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRequestCost","value":"[\"Free\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/deliveryLeadTime","value":"LESS 1 WEEK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"GB-ENG\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataProcessor","value":"HDR UK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"HDR UK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"en\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"CSV\",\"JSON\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/qualifiedRelation","value":"[\"https://web.www.healthdatagateway.org/dataset/fd8d0743-344a-4758-bb97-f8ad84a37357\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/derivation","value":"[\"https://web.www.healthdatagateway.org/dataset/fd8d0743-344a-4758-bb97-f8ad84a37357\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/tools","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"observedNode\":\"FINDINGS\",\"measuredValue\":575,\"disambiguatingDescription\":\"Number of papers with affiliation and/or acknowledgement to HDR UK\",\"observationDate\":\"27/11/2020\",\"measuredProperty\":\"Count\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[{"label":"papers","description":"HDR UK Paper and Preprints","childDataElements":[{"label":"urls","description":"List of URLS (DOI, HTML, PDF)","dataType":{"label":"List (URLS)","domainType":"PrimitiveType"}},{"label":"date","description":"Date of Publication","dataType":{"label":"Date","domainType":"PrimitiveType"}},{"label":"date","description":"Date of Publication1","dataType":{"label":"Date","domainType":"PrimitiveType"}}]}]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/61d6d831050b530d73689c96.json b/src/controllers/datasetfiles/61d6d831050b530d73689c96.json deleted file mode 100644 index ab9ae485..00000000 --- a/src/controllers/datasetfiles/61d6d831050b530d73689c96.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"New dataset 6 Jan 2022 11:53","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"New dataset 6 Jan 2022 11:53"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/61fab5b554d6b45d5fa72b58.json b/src/controllers/datasetfiles/61fab5b554d6b45d5fa72b58.json deleted file mode 100644 index 9f4ad94b..00000000 --- a/src/controllers/datasetfiles/61fab5b554d6b45d5fa72b58.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"Test jag 1","description":"testwqdwdw","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/coverage/spatial","value":"[\"United Kingdom,England,Eastern,Colchester\"]"},{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"Test jag 1"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"testwqdwdw"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"jag.kandasamy@gmail.com"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Test\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"observedNode\":\"PERSONS\",\"measuredValue\":\"22344\",\"observationDate\":\"01/03/2022\",\"measuredProperty\":\"1234\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/61fab8de2f6ba75e6eda6bff.json b/src/controllers/datasetfiles/61fab8de2f6ba75e6eda6bff.json deleted file mode 100644 index 58fee089..00000000 --- a/src/controllers/datasetfiles/61fab8de2f6ba75e6eda6bff.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"test jag 1","description":"wdwdwewef","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"test jag 1"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"wdwdwewef"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"jag.kandasamy@gmail.com"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"exercise challenge test\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/spatial","value":"[\"United Kingdom,England,Eastern,Colchester\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"IRREGULAR"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2022-03-01"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"LESS 1 WEEK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"https://localhost:3000/dataset-onboarding\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"GB-ENG\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"12333ferfrgrg"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"OPCS4\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"HL7 V2\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"ab\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"123444\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"observedNode\":\"PERSONS\",\"measuredValue\":\"wdweefefef\",\"observationDate\":\"01/03/2022\",\"measuredProperty\":\"weefefeferf\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/6231ecea2e022b2e76da14aa.json b/src/controllers/datasetfiles/6231ecea2e022b2e76da14aa.json deleted file mode 100644 index ae57af81..00000000 --- a/src/controllers/datasetfiles/6231ecea2e022b2e76da14aa.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"Mental Health Dataset (MHDS) for CPRD Aurum123","description":"CPRD Aurum linked Mental Health Dataset (MHDS) is a collection of patient records of individuals who accessed secondary care adult mental health services and who are thought to be suffering from a mental illness. The data include information about the type and location of care received, different episodes of care received within a spell of illness and the events that occurred such as recording of Health of the Nation Outcome Scales (HoNOS) scores, Patient Health Questionnaire (PHQ-9) scores or diagnoses. MHDS data can be used to support research into resource utilisation and provide information about patient access to secondary mental health care services. This can be useful to understand patient pathways and consider associations between primary care and access to and outcomes recorded in secondary mental health care services.","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"Mental Health Dataset (MHDS) for CPRD Aurum123"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"CPRD Aurum linked Mental Health Dataset (MHDS) data contain the type and location of care received, episodes of care, diagnoses and questionnaire scores for individuals who accessed secondary care adult mental health services in England."},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"enquiries@cprd.com"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Mental health\"]"},{"namespace":"org.healthdatagateway","key":"properties/summary/doiName","value":"https://doi.org/10.48329/4v6r-zd41"},{"namespace":"org.healthdatagateway","key":"properties/documentation/description","value":"CPRD Aurum linked Mental Health Dataset (MHDS) is a collection of patient records of individuals who accessed secondary care adult mental health services and who are thought to be suffering from a mental illness. The data include information about the type and location of care received, different episodes of care received within a spell of illness and the events that occurred such as recording of Health of the Nation Outcome Scales (HoNOS) scores, Patient Health Questionnaire (PHQ-9) scores or diagnoses. MHDS data can be used to support research into resource utilisation and provide information about patient access to secondary mental health care services. This can be useful to understand patient pathways and consider associations between primary care and access to and outcomes recorded in secondary mental health care services."},{"namespace":"org.healthdatagateway","key":"properties/documentation/associatedMedia","value":"[\"https://www.cprd.com/linked-data\"]"},{"namespace":"org.healthdatagateway","key":"properties/documentation/isPartOf","value":"CPRD Aurum"},{"namespace":"org.healthdatagateway","key":"properties/coverage/spatial","value":"[\"England\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/typicalAgeRange","value":"0-115"},{"namespace":"org.healthdatagateway","key":"properties/coverage/physicalSampleAvailability","value":"[\"NOT AVAILABLE\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/followup","value":"1 - 10 YEARS"},{"namespace":"org.healthdatagateway","key":"properties/coverage/pathway","value":"Secondary care"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/purpose","value":"[\"CARE\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/source","value":"[\"EPR\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/collectionSituation","value":"[\"CLINIC\",\"COMMUNITY\",\"IN-PATIENTS\",\"OUTPATIENTS\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"QUARTERLY"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/distributionReleaseDate","value":"2020-04-21"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2007-04-01"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/endDate","value":"2015-11-30"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"MORE 6 MONTHS"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseLimitation","value":"[\"GENERAL RESEARCH USE\",\"NO LINKAGE\",\"RESEARCH SPECIFIC RESTRICTIONS\",\"RESEARCH USE ONLY\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseRequirements","value":"[\"GEOGRAPHICAL RESTRICTIONS\",\"INSTITUTION SPECIFIC RESTRICTIONS\",\"PROJECT SPECIFIC RESTRICTIONS\",\"TIME LIMIT ON USE\",\"USER SPECIFIC RESTRICTION\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/resourceCreator","value":"[\"NHS Digital\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/investigations","value":"[\"https://www.cprd.com/protocol-list\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/isReferencedBy","value":"[\"[]\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"HTTPS://CPRD.COM/DATA-ACCESS\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessService","value":"Access to CPRD data, including UK Primary Care Data, and linked data such as Hospital Episode Statistics, is subject to protocol approval via CPRD’s Research Data Governance (RDG) Process. Independent scientific and patient advice is provided by Expert Review Committees (ERCs) and the Central Advisory Committee (CAC): https://www.cprd.com/research-applications"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRequestCost","value":"[\"https://www.cprd.com/pricing\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/deliveryLeadTime","value":"VARIABLE"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"GB-ENG\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataProcessor","value":"CPRD"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"Clinical Practice Research Datalink (CPRD)"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"NHS DATA DICTIONARY\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"en\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"Tab delimited text\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/qualifiedRelation","value":"[\"CPRD Aurum\",\"CPRD Aurum SGSS\",\"CPRD Aurum CHESS\",\"CPRD Aurum ICNARC\",\"Death Registration data for CPRD Aurum\",\"HES Admitted Patient Care data for CPRD Aurum\",\"HES Outpatient data for CPRD Aurum\",\"HES Accident and Emergency data for CPRD Aurum\",\"HES Diagnostic Imaging Dataset for CPRD Aurum\",\"Patient postcode linked deprivation measures for CPRD Aurum\",\"Practice postcode linked deprivation measures for CPRD Aurum\",\"Rural-Urban classification for CPRD Aurum\",\"Cancer registration data for CPRD Aurum\",\"National Radiotherapy Dataset (RTDS) for CPRD Aurum,Systemic Anti-Cancer Treatment (SACT) data for CPRD Aurum\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/derivation","value":"[\"N/A\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/tools","value":"[\"[]\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"observedNode\":\"PERSONS\",\"measuredValue\":2080806,\"disambiguatingDescription\":\"Number of CPRD Aurum patients matched to single MHDS identifier\",\"observationDate\":\"21/04/2020\",\"measuredProperty\":\"Count\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[{"label":"Episode","description":"A85","childDataElements":[{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key, in combination with e_care_record_spell & e_episode_id]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"e_episode_id","description":"CPRD generated unique identifier for each episode [primary key, in combination with patid & e_care_record_spell]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"ic_inactive_timestamp","description":"","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"data1","description":"Dependent on epitype","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"epistart_date","description":"Episode start date. Replaced with delayed_dischstart_date if missing, and epitype is ‘DDISC’, or refrec_date if missing and epitype is ‘REF’.","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"e_care_record_spell","description":"CPRD generated unique identifier for each record spell of care [primary key, in combination with patid & e_episode_id]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data2","description":"Dependent on epitype","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"epiend_date","description":"Episode end date. Replaced with delayed_dischend_date if missing.","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"epitype","description":"Episode type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}}]},{"label":"Event","description":"A94","childDataElements":[{"label":"data15","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data13","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"eventdate","description":"Date of event","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"data4","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data3","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"e_event_id","description":"CPRD generated unique identifier for each event [primary key, in combination with patid & e_care_record_spell]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data2","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data16","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data9","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data18","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data22","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"e_care_record_spell","description":"CPRD generated unique identifier for each record spell of care [primary key, in combination with patid & e_event_id]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key, in combination with e_care_record_spell & e_event_id]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data10","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data12","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data8","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data20","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data5","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data7","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data6","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data14","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data17","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data1","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data21","description":"Dependent on event type","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"eventtype","description":"Type of event recorded","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"data11","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"data19","description":"Dependent on event type","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}}]},{"label":"HoNOS","description":"A55","childDataElements":[{"label":"honos_date","description":"Date of HoNOS score","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"honos_type","description":"Type of HoNOS event reported on [1 = First, 2= Most recent in period, 3=Worst ever, 4=Best in last 12 months]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"honos","description":"HoNOS score encoded as 13-character string (Each character position refers to a given category. E.g. The 1st character refers to 'overactive, aggressive, disruptive or agitated behaviour'. Each character value encodes the severity of the problem. The category of the most severe clinical problem being rated by items 8-12 is indicated by a letter at position 8). See glossary for HoNOS scoring here: http://bjp.rcpsych.org/content/bjprcpsych/174/5/432.full.pdf","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}}]},{"label":"Patient","description":"A2","childDataElements":[{"label":"n_patid_mh","description":"Number of individuals in CPRD GOLD or CPRD Aurum assigned the same gen_mhds_id","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_mhds_id","description":"MHDS patient key. A generated unique key assigned to a patient in the MHDS data. An individual that has contributed data to more than one CPRD practice has the same gen_mhds_id.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"pre_2011","description":"Patient present in Format 1 data (01/04/2007 and 31/03/2011)","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_firstknown","description":"CPRD derived variable indicating the year in which the patient is first known to have received care from any mental health service","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_manpsych_date","description":"CPRD derived earliest record of manifest psychosis. This is the date at which a positive psychotic symptom has lasted for a week","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"gen_psychtreat_start_date","description":"CPRD derived earliest record of the patient starting prescribed anti-psychotic medication and remaining compliant for at least 75% of the time during the subsequent month (based on clinical judgment).","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"pracid","description":"Encrypted unique key given to a practice in CPRD GOLD or CPRD Aurum","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_propsych_date","description":"CPRD derived earliest record of prodromal psychosis, indicating the first noticeable change in behaviour or mental state prior to emergence of full-blown psychosis","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"gen_psychpresc_date","description":"CPRD derived earliest record of a prescription for an anti-psychotic medication","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"post_2011","description":"Patient present in Format 2 data (01/04/2011 and 30/11/2015)","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_ethnicity","description":"CPRD derived patient ethnicity","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"gen_emerpsych_date","description":"CPRD derived earliest record of emergent psychosis indicating the first clear evidence of a positive psychotic symptom for the patient (i.e. delusion, hallucination, or thought disorder)","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"match_rank","description":"Indicates the quality of matching between a record in MHDS and CPRD primary care data and gives the level of confidence that an MHDS record has been correctly matched to a patient in CPRD GOLD or CPRD Aurum.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}}]},{"label":"Period","description":"A16","childDataElements":[{"label":"admissions","description":"Total number of admissions within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"period","description":"The financial year that the record represents","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"cpa_enhanced_days","description":"The number of days that the Care Programme Approach is at the Enhanced level: 'multiple needs, including housing, employment etc, which requires inter-agency coordination'","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"care_programme_approach_reviews","description":"Total number of care programme approach reviews within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_community_psych_nurse","description":"Total number of contacts with the community psychiatric nurse group.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"outpatient_attendance_consultant","description":"Outpatient consultant appointment attendance","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"sheltered_work_attendance_ind","description":"Binary indicator of whether or not one or more sheltered work attendances or sessions occurred within the reporting period (0 = no attendance at a sheltered work facility; 1=one or more attendances at a sheltered work facility).","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_occupational_therapist","description":"Total number of contact with an occupational therapist within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"cpa_standard_days","description":"The number of days that the Care Programme Approach is at Standard level: 'requires the support or intervention of one agency or discipline; or requires only low-key support from more than one agency’","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"bed_days_mh","description":"Total bed days","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_social_worker","description":"Total number of face to face contacts with a social worker within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"daycare_attend_mh_nhssite","description":"Total number of attendances to a day care facility where the function classification is 'mental illness'. Facility are either solely NHS run or jointly run between NHS and non-NHS organisations.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"daycare_attend_mh_nonnhssite_ind","description":"Binary indicator of whether the patient attended a solely non-NHS funded day care facility with the function classification 'mental illness'.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"bed_days_nhs_community_care","description":"Total number of bed days in a care home site operated and managed by an NHS organisation where the broad patient group is 'Patients with mental illness'.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_nhsdirect_mh","description":"Total number of contacts with NHS direct that NHS direct are required to share with a Mental Health Trust within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"sssa_number_community_care","description":"Number of social services statutory assessments for community care within the reporting period.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"residential_mh_non_nh_ccare_ind","description":"Binary indicator of the occurrence of residential community mental health care provided by a non-NHS provider. (0 = no stays in non-NHS care home during the reporting period; 1= one or more stays in non-NHS care home during reporting period).","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_physiotherapist","description":"Total number of contacts with a physiotherapist within reporting period.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"bed_days_mh_intensive","description":"Total number of bed days where the patient was admitted to a 'ward with a clinical care intensity ('specially designated ward for patients needing containment and more intensive management') where the main treatment is for an adult mental illness. This includes hospital stays and care home stays in consultant care within the hospital provider spell.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"social_worker_involvement","description":"Binary indicator of whether a patient's care plan includes involvement with local authority social services social workers. (0 = no involvement of social worker; 1 = involvement of social worker)","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"care_days_acute_home_based","description":"Total number of acute home-based care days","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"discharges","description":"Total number of discharges within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"day_care_dna_mh_nhssite","description":"Total number of 'Did not attend - no advance warning given' classifications on day care sessions within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_clinical_psychologist","description":"Total number of contacts with a clinical psychologist within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"bed_days_mh_medium_secure[1]","description":"Total number of bed days where the patient was admitted to an 'organisation site, service point or ward with a secure accommodation type classification b: 'medium secure accommodation, a secure facility providing care at a regional level under the care of a forensic psychiatrist' ' where the main treatment is for an adult mental illness. This excludes high security accommodation in hospital site approved to provide high security psychiatric services.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"ect_treatments_administered","description":"Total number of Electro-Convulsive Therapy (ECT) administrations within the reporting period.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"outpatient_dna_mh","description":"Total number of 'Did not attend - no advance warning given' classifications on outpatient appointments within the reporting period","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"contacts_consultant_psychotherapy","description":"Total number of contacts with a consultant psychotherapist within reporting period.","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"home_help_visit_ind","description":"Binary indicator of whether a home help visit occurred during the reporting period (0=no home help visit occurred; 1= one or more home help visits occurred)","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}}]},{"label":"Record","description":"A59","childDataElements":[{"label":"pcp_memorychild_score","description":"Patient provided score of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty with memory or ability to concentrate, learn or understand which began before the age of 18. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"smokstat","description":"Smoking status [1 = Current smoker; 2= Ex-smoker; 3= Non-smoker – history unknown; 4= Never smoked; Z= Not stated]. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_disability_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_autism_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). Added 2014","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"gen_crisisplancreat_date","description":"Crisis plan creation date – CPRD derived using latest crisisplancreat_date within spell","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"pcp_personal_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty keeping clean and dressing the way they would like to. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_sight_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty seeing signs or things printed on paper, or seeing things at a distance. Added 2014","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"pcp_memoryadult_score","description":"Patient provided score of perception emotional disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty with memory or ability to concentrate, learn or understand which began after the age of 18. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_manual_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient experiences difficulty performing tasks with their hands. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"gen_statusdate_smoking","description":"Smoking status date - CPRD derived from latest statusdate_smoking within spell","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"e_code_of_provider","description":"Encoded code for care provider","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"pcp_mobility_score","description":"Patient provided score of perception of mobility disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty getting around physically without assistance or needs aids like wheelchairs or walking frames; or where the patient has difficulty controlling how their arms, legs or head move. Added 2014","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"pcp_danger_score","description":"Patient provided score of perception disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty understanding that some things, places or situations can be dangerous and could lead to a risk of injury or harm. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_hearing_score","description":"Patient provided score of perception of hearing disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty hearing, or need hearing aids, or need to lip-read what people say. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"marital_status","description":"Marital status","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_other_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has any other important health issue. Added 2014","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"ethnicity","description":"Original ethnicity","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_progressive_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has any illness which affects what they can do, or which is making the more ill, which is getting worse, and which is going to continue getting worse. Added 2014","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"health_care_spell_end_reason","description":"Present if mental health care spell ended before end date. It provides the reason for ending the care spell. [00 = Finished on professional advice; 01 = Finished against professional advice; 02 = Finished by patient's non-attendance; 03 = Patient died; 21 = Transfer to medium secure; 23 = Transfer to high secure; 25 = Transfer to other health provider]","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"pcp_behem_score","description":"Patient provided score of perception of behaviour and emotional disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has times when they lack control over their feelings or actions. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"e_care_record_spell","description":"CPRD generated unique identifier for each record spell of care [primary key, in combination with patid]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"start_date_mhcs","description":"Start date of mental health care spell","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key, in combination with e_care_record_spell]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"pcp_speech_score","description":"Patient provided score of perception of disability impact (Disability Impact Perception) (01 = Yes -limited a lot; 02 = Yes - limited a little; 03 = No- not limited; 04 = Prefer not to say). This disability category is used where the patient has difficulty speaking or using language to communicate or make their needs known. Added 2014","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"end_date_mhcs","description":"End date of mental health care spell","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"gen_crisisplanupdate_date","description":"Crisis plan update date - CPRD derived using latest crisisplanupdate_date within spell","dataType":{"label":"DATE","domainType":"PrimitiveType"}}]},{"label":"Spell","description":"A46","childDataElements":[{"label":"marital_status","description":"Recorded marital status","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"e_code_of_commissioner","description":"Encoded code for commissioner","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"patid","description":"Encrypted unique key given to a patient in CPRD GOLD or CPRD Aurum [primary key, in combination with e_care_spell_identifier]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"start_date_mhcs","description":"Mental health care spell start date","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"Ethnicity","description":"Recorded ethnicity","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"e_care_spell_identifier","description":"CPRD generated unique identifier for each spell of care [primary key, in combination with patid]","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}},{"label":"end_date_mhcs","description":"Mental health care spell end date","dataType":{"label":"DATE","domainType":"PrimitiveType"}},{"label":"spell_definition_type","description":"Defines methods used to derive start and end date of spell (A: derived from the succession of recorded contacts; D: declared explicitly; DX: declared explicitly but the dates have been adjusted to accommodate contacts that occurred outside of declared or derived dates; E: derived from the collation of recorded episodes; EX: derived from the collation of recorded episodes but adjusted for contacts that occurred outside these dates)","dataType":{"label":"CHAR","domainType":"PrimitiveType"}},{"label":"e_code_of_provider","description":"Encoded code for care provider","dataType":{"label":"INTEGER","domainType":"PrimitiveType"}}]}]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/6239a9eba80b5bb3563911c6.json b/src/controllers/datasetfiles/6239a9eba80b5bb3563911c6.json deleted file mode 100644 index 05804531..00000000 --- a/src/controllers/datasetfiles/6239a9eba80b5bb3563911c6.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"New dataset 21 Mar 2022 10:15-Jag test 123","description":"Test documentaion","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"New dataset 21 Mar 2022 10:15-Jag test 123"},{"namespace":"org.healthdatagateway","key":"properties/documentation/description","value":"Test documentaion"},{"namespace":"org.healthdatagateway","key":"properties/documentation/associatedMedia","value":"[\"https://imawefefef.com\",\"http://localhost:3000/dataset-onboarding/62385af3a80b5bb35638e67d\",\"https://www.bbc.co.uk/news\"]"},{"namespace":"org.healthdatagateway","key":"properties/documentation/isPartOf","value":"testst "},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[{"label":"PDR28Admission","description":"Spell admission details","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"admission_type_translated","description":"States whether spell is elective or emergency","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"arrival_datetime","description":"Datetime at which the CareSpell started","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"arrival_datetime_id","description":"dim.Time record ID for arrival_datetime","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"discharge_datetime","description":"Datetime at which the CareSpell ended. Null is still ongoing","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"discharge_datetime_id","description":"dim.Time record ID fordischarge_datetime","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"admission_location_translated","description":"Admission type (Daycase, Elective,Emergency etc)","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"discharge_location_translated","description":"Outcome of the carespell (discharged home, transferred out etc)","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ethnicity","description":"Patient's ethnicity description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"age","description":"Patient's age at the time of admission","dataType":{"label":"numeric","domainType":"PrimitiveType"}},{"label":"quintile","description":"Index of multiple deprivation quintile score based off postcode (1 is most deprived 5 is least deprived)","dataType":{"label":"float","domainType":"PrimitiveType"}},{"label":"sex","description":"Patient's gender","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"dod","description":"Patient's date of death","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Assessment","description":"Assesment details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"assessment_id","description":"Unique identifier of the assessment","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practitioner who performed the patient's assessment","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"assessment_source","description":"Assessment source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"assessment","description":"Assessment description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form_set_id","description":"Identifier for the set from which this assessment was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form_name","description":"Name of the form","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"field_description","description":"Field description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"assessment_recorded","description":"Datetime of when the assessment was recorded","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"assessment_displayed","description":"Datetime of when the assessment was displayed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"source_value","description":"Assessment results","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_value_expn","description":"Assessment results expansion","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28AttendingPractitionerChange","description":"Attending Practitioner Change details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"change_id","description":"Unique identifier for this change in attending practitioner","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practitioner who is now overseeing the patient's care","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"speciality","description":"Specility under which the patient is being treated (not necessarily the same as the speciality of the practitioner)","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_changed","description":"The time at which the change in attending practitioner occurred","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28BedMove","description":"Bed Move details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"bed_move_id","description":"Unique identifier for this bed move record","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"bed_no","description":"Unique identifier for this bed","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"bed_arrival_time","description":"Datetime at which the patient was moved to this bed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"bed_discharged_time","description":"Datetime at which the patient was discharged from this bed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"arrival_practitioner_id","description":"Identifier of the practitioner who is overseeing the patient's bed arrival","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"discharged_practitioner_id","description":"Identifier of the practitioner who is overseeing the patient's bed discharge","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ward","description":"Description of the ward","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28CallSign","description":"Call sign details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"call_sign_id","description":"Unique identifier for call sign","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"arrival_datetime","description":"Datetime of the ambulance arrival","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"crew_call_sign","description":"Crew call sign number","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Consultation","description":"Consultation details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_id","description":"Unique identifier for the consultation","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practitioner who performed the consultation","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"consultation_start","description":"Datetime the consultation started","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"consultation_end","description":"Datetime the consultation ended","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"directorate","description":"Description of the directorate","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_status","description":"States whether the patient is present or not present","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_type","description":"Description of the consulation type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Diagnosis","description":"Diagnosis details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_id","description":"Unique identifier for the diagnosis","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_source_code","description":"Diagnosis source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_source_code_expn","description":"Diagnosis source code description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis","description":"The concept code translated to snomed ct or icd10","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_name","description":"Name of the diagnosis","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"coding_system","description":"States whether the diagnosis is snomed or icd 10","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_type","description":"Description of the diagnosis type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"diagnosis_recorded","description":"Datetime of when the diagnoses was recorded","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"diagnosis_order","description":"The precidence of the diagnosis","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"recorded_diagnosis_start","description":"Datetime that the diagnosis was recorded to have begun (start of episode for clinical coding, start of condition where available otherwise)","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"recorded_diagnosis_end","description":"Datetime that the diagnosis was recorded to have ended (end of episode for clinical coding, end of condition where available otherwise)","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28DnarTeal","description":"Dnar Teal details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"dnar_teal_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"recorded_time","description":"Datetime of when then dnar/teal was recorded","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"expired_time","description":"Datetime at which the dnar/teal was invalidated by the epr system","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practitioner who performed the dnar/teal assessment","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"dnar_teal_parameter_code","description":"Dna/teal source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"dnar_teal_parameter","description":"Dnar/teal source code description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_value","description":"Results value of the dnar/teal assessment","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_value_expn","description":"Results value of the dnar/teal assessment description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28DrugAdmin","description":"Drug Admin details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_administration_id","description":"Unique identifier for the drug administration event","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"prescription_id","description":"Identifier for the prescription","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"given_by_id","description":"Practitioner who administered the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_prescribed","description":"Datetime when drug was due to be administered","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_administered","description":"Datetime at which the drug administration began","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"administration_status_code","description":"Administration status code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"administration_status","description":"Whether or not the prescription was administered description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form_code","description":"Form of the drug code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form","description":"Drug form description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"route_code","description":"Route of administration code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"route","description":"Route description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"quantity","description":"Quantity of the drug being administered","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"quantity_unit","description":"Units of quantity","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"concentration","description":"Concentration of the drug's active ingredient, e.g. 0.5%, 10mmol/l","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"concentration_unit","description":"Units of the concentration of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"infusion_rate","description":"Prescribed infusion rate of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"infusion_rate_unit","description":"Prescribed units of infusion rate of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"volume_remaining","description":"Total volume of the drug being administered","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"volume_remaining_unit","description":"Units of the total volume of the drug being administered","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"own_medication","description":"Flag whether patient has their own medication","dataType":{"label":"bit","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28DrugAllergy","description":"Drug Allergy details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"allergen_id","description":"Unique allergen identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"allergen","description":"Name of the drug allergen","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"allergen_type","description":"Allergen type code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"type_description","description":"Describes the type description, whether its ' drug class' or ' generic drug'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"status","description":"Status code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"status_description","description":"Describes the status description, whether its 'complete' or 'failed'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"declared_time","description":"Datetime of when the allergen was declared","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"declared_time_id","description":"Dim.time record identifier for the time the drug allergy was declared","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"cancelled_time","description":"Datetime of when the allergen was cancelled","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"cancelled_time_id","description":"Dim.time record identifier for the time the drug allergy was cancelled","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"practitioner_declared_id","description":"Identifier of the practitioner who declared the drug allergy assessment","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"practitioner_cancelled_id","description":"Identifier of the practitioner who cancelled the drug allergy assessment","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28DrugComponent","description":"Drug Component details for each spell","childDataElements":[{"label":"prescription_component_id","description":"Unique identifier for prescription component details","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"prescription_id","description":"Identifier for the prescription","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_code","description":"Drug source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug","description":"Name of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form_code","description":"Drug form source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"form","description":"Description of the drug form","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_form_code","description":"Drug alternative form source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_form","description":"Description of the drug alternative form","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"quantity","description":"Quantity of the drug being prescribed","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"quantity_unit","description":"Description of the drug quantity","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_quantity","description":"Alternative quantity of the drug being prescribed","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_quantity_unit","description":"Units of alternative quantity","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"concentration","description":"Concentration of the drug's active ingredient, e.g. 0.5%, 10mmol/l","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"concentration_unit","description":"Units of concentration of the drug's active ingredient","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_concentration","description":"Prescribed alternative concentration of the drug's active ingredient, e.g. 0.5%, 10mmol/l","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_concentration_unit","description":"Units of the prescribed alternative concentration of the drug's active ingredient","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"infusion_rate","description":"Description of planned infusion rate","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"min_infusion_rate","description":"Minimum prescribed infusion rate of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"max_infusion_rate","description":"Maximum prescribed infusion rate of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"infusion_rate_unit_code","description":"Description of infusion rate unit","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"infusion_rate_unit_code_expn","description":"Prescribed infusion rate of the drug when scaled by an attribute of the patient such as mg/kg/hr","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"scale_infusion_rate","description":"Scaled infusion rates of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"min_scale_infusion_rate","description":"Minimum prescribed infusion rate of the drug when scaled by an attribute of the patient such as mg/kg/hr","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"max_scale_infusion_rate","description":"Maximum prescribed infusion rate of the drug when scaled by an attribute of the patient such as mg/kg/hr","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"scale_infusion_rate_unit","description":"Units of the scaled infusion rates of the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Electrocardiogram","description":"Electrocardiogram details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"electrocardiogram_id","description":"Unique identifier for the electrocardiogram","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"master_ecg_id","description":"Unique identifier for the parent electrocardiogram","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ecg_time","description":"Datetime of the electrocardiogram procedure","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"ecg_type","description":"Description of the electrocardiogram type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"file_type","description":"Describes the file type,whether its 'jpg,'pdf' or 'hdcxml'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"device_model","description":"Description of the device model","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Event","description":"Event details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"event_id","description":"Unique identifier for the event","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"event_time","description":"Datetime the event was entered","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"event_source","description":"Event source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"event","description":"Source code description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28FollowUp","description":"Follow up details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"follow_up_id","description":"Unique identifier of the follow up","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"set_id","description":"Identifier for the set from which this follow up was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"follow_up_source","description":"Follow up source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"follow_up","description":"Follow up description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practioner who perfomed the follow up","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_performed","description":"Datetime when the follow up wa performed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_completed","description":"Datetime when the follow up was completed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"source_result","description":"Follow up results","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"report","description":"Follow up report details","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Image","description":"Image details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"image_id","description":"Unique Identifier for the image","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"bigint","domainType":"PrimitiveType"}},{"label":"requestor_id","description":"Practitioner that requested the image be taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"request_time","description":"The datetime at which the image was requested","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"image_time","description":"Datetime of the Image complete","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"site_id","description":"Site Id","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"image_type","description":"Image type description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"examination_type","description":"Examination type description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28LabAnalysis","description":"Lab Analysis details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"lab_analysis_id","description":"Unique Identifier of lab analysis measurement","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"sample_id","description":"Identifier for the specimen from which this analysis was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"sample_type_source","description":"Lab analysis type source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"sample_type","description":"Description of the lab analysis type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"lab_analysis_source","description":"Lab analysis source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"lab_analysis","description":"Name of the lab analysis","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"request_practitioner","description":"Identifier of the practioner who requested the lab sample","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"sample_collected_datetime","description":"Datetime when the sample was collected from the patient","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"sample_received_datetime","description":"Datetime when the sample was received by labs","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"sample_result_datetime","description":"Datetime when sample result authorised by consultant (usually) and released","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"result","description":"Lab analysis result","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"unit","description":"Units of the lab analysis result","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"lab_analysis_translated","description":"Lab Analysis translated value","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28MedicalProcedure","description":"Medical Procedure details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"medical_procedure_id","description":"Unique Identifier of the Medical Procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"procedure_code","description":"The OPCS4 or SNOMED code for the procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"procedure_order","description":"The precedence of the procedure","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"procedure_start","description":"Datetime of when the procedure started","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"scid","description":"SNOMED concept id of the procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"opcs_code","description":"OPCS4 code of the procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"procedure_code_expn","description":"Name of the procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28MicroBiology","description":"Microbiology details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"micro_biology_id","description":"Unique Identifier of the microbiology sample","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"specimen_id","description":"Identifier for the specimen from which this microbiology sample was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_collected","description":"Datetime the Microbiology was collected","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_displayed","description":"Datetime the results of the Microbiology test were made available to pratitioners","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"specimen_taken_location","description":"Hospital site where sample was taken from","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"specimen_taken_location_desc","description":"Ward Name where sample was taken from","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"specimen_type","description":"Describes the specimen type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"site_of_specimen","description":"Anatomic site of where sample was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"organism_source","description":"Organism source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"organism","description":"Describes the organism","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"antibiotic_source","description":"Antibiotic source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"antibiotic","description":"Describes the antibiotic","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"result","description":"Result value of the microbiology tests","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"organism_translated","description":"Organism translated","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"antibiotic_translated","description":"Antibiotic translated","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28Observation","description":"Observation details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"observation_id","description":"Unique identifier of observation","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"set_id","description":"Identifier for the specimen from which this observation was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"observation_code","description":"Observation source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"observation","description":"Name of the observation","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_performed","description":"Datetime when the observation was performed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_completed","description":"Datetime when the observation was completed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"result","description":"Observation result","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"unit","description":"Units of the result","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"scid","description":"SNOMED concept id for the observation","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"scid_expn","description":"SNOMED concept description for the observation","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28OutpatientsAdmission","description":"Outpatients Admissions details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"opd_admission_detail_id","description":"Unique identifier for the outpatients admission detail","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"clinic_code","description":"Clinic code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"clinic","description":"Clinic name","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"attended_dna_source","description":"Attended/dna status source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"attended_dna","description":"Attended/dna status description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"priority_type_source","description":"Priority type source","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"priority_type","description":"Priority type description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"appointment_time","description":"Datetime of the patient's appointment","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"postcode","description":"Patient's postcode","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"gp_code","description":"Patient's gp_code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"walk_in","description":"Describes the patient's walkin status, values are either 'y' or 'n'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_media_source","description":"Method of consultation source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_media","description":"Description of method of consultation","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_outcome_source","description":"Outcome source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"consultation_outcome","description":"Description of consultation outcome","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Prescription","description":"Prescription details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"prescription_id","description":"Identifier of the prescription","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"prescription_type","description":"Prescription description (\"PREVIOUS\", \"IN-HOSPITAL\", \"TTO\")","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"written_time","description":"Time the prescription was completed by the practitioner","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"start_time","description":"Time at which the drug administrations for this prescription were expected to start","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practitioner who prescribed the drug","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"pharmacist_signoff_time","description":"Date/time at which pharmacy signed off the patient's prescription","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"pharmacist_signoff_time_id","description":"dim.Time record ID for pharmacist_signoff_time","dataType":{"label":"char","domainType":"PrimitiveType"}},{"label":"mode_code","description":"Prescription mode source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"mode","description":"Describes the prescribed mode of administration, e.g. Continuous, As Required, Once Only, Regular","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"frequency_code","description":"Frequency source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"frequency","description":"Plain-text description of the prescribed frequency of drug administration","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"route_code","description":"Route source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"route","description":"Route of administration","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_route_code","description":"Alternative route source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"alternative_route","description":"Prescribed alternative route of administration description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_set_code","description":"Drug set source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_set","description":"Drug set group description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_set_package_code","description":"Drug set package source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"drug_set_package","description":"Drug set package description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Referral","description":"Referral details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"referral_id","description":"Unique identifier for the referral","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"referral_made","description":"Datetime of when the referral was made","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"referral_to_source","description":"Referral to location source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"referral_to","description":"Referral to location description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"referral_to_translated","description":"Referral translated","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28SiteMove","description":"Site Move details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"site_move_id","description":"Unique identifier for this sitemove record","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"site_source","description":"Site source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"site","description":"Site description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"host_site","description":"Name of the host site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"move_time","description":"The time at which the move occurred","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Surgery","description":"Surgery details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"surgery_id","description":"Unique identifier for the surgery","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Unique identifier of the practitioner who performed the surgery","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"theatre_code","description":"Theatre source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"theatre","description":"Site where the surgery took place","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_sent_for","description":"Time the patient was sent for","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_into_anaesthetics","description":"Time the patient arrived to be aneasthetised","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_induced","description":"Time the patient was anaesthetised","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_into_theatre","description":"Time patient entered the operating theatre","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_knife_to_skin","description":"Time that the first incision began","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_closed","description":"Time the incision was closed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_recovery_start","description":"Time the patient was put into recovery","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_handover","description":"Time of anaesthetics handover to ward stafff","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"recovery_ward_code","description":"Recovery ward source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"recovery_ward","description":"Ward to which the patient was handed over from anaesthetics","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28SurgeryProcedure","description":"Surgery Procedure details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"surgery_procedure_id","description":"Unique identifier for the surgical procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"surgery_id","description":"Unique identifier for the surgery session","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practioner who performed the surgery","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"procedure_code","description":"OPCS4 code for the procedure","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"procedure_code_order","description":"The precedence of the procedure","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"procedure_start","description":"Datetime of when the procedure started","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"procedure_end","description":"Datetime of when the procedure ended","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"procedure_code_expn","description":"Procedure Name","dataType":{"label":"varchar","domainType":"PrimitiveType"}}]},{"label":"PDR28Therapy","description":"Therapy details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"therapy_contact_id","description":"Unique identifier for the therapy","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"therapy_set_id","description":"Identifier for the set from which this analysis was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"therapy_source","description":"Therapy source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"therapy","description":"Therapy source description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_logged","description":"Datetime of the contact status inv","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"therapy_type","description":"Describes the type of therapy, whether it was 'podiatry','manual handling','speech and language therapy','dietetics','occupational therapy','physiotherapy'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"contact_type","description":"Describes the contact status, whether it was 'new referral','follow-up','discharge'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"contact_priority","description":"Describes the contact priority, whether it was 'red','amber','green' or 'null'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"reason_priority_changed","description":"Describes the reason for the priority change, whether it was 'inaccurate referral information','patient condition changed','intervention has changed' or 'null'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_type","description":"Describes the patient type such as 'nhs day case','nhs inpatient','private day case','rcdm inpatient','private inpatient'","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"reason_therapy_ended","description":"Describes the reason on why the theraphy ended","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"number_of_staff","description":"Describes the number of staff","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"contact_15min_blocks","description":"How many single 15-minute blocks of face-to-fact time was used for this encounter","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"indirect_15min_blocks","description":"How many 15-minute blocks away from the patient did the therapist(s) use for this encounter","dataType":{"label":"int","domainType":"PrimitiveType"}},{"label":"intervention","description":"Description of the intervention","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"audit_type","description":"Description of the audit type","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"on_call_activity","description":"Whether this encounter was with an on-call therapist","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"valid_call_out","description":"Whether an on-call encounter was necessary","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"goal_set","description":"Whether new goals were set during the encounter","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"goal_updated","description":"Whether any existing goals were updated during the encounter","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"practitioner_id","description":"Identifier of the practioner who logged the therapy contact","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Triage","description":"Triage details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"triage_id","description":"Unique identifier for triage","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"triage_problem","description":"Snomed concept code of the presenting problem","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"triage_time","description":"Datetime of when triage was performed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"triage_level","description":"Snomed concept code of the triage level","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"attendance_type","description":"Attendance acuity level","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"hospital_site","description":"Name of the hospital site","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]},{"label":"PDR28Ventilation","description":"Ventilation details for each spell","childDataElements":[{"label":"care_spell_id","description":"Unique identifier for the spell","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ventilation_id","description":"Unique identifier for ventilation parameter","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"patient_pseudo_id","description":"Hashed patient identification","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"set_id","description":"Identifier for the set from which this ventitlation assessment was taken","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ventilation_parameter_source_code","description":"Ventilation parameter source code","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"ventilation_parameter","description":"Ventilation parameter description","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"time_performed","description":"Datetime when the ventilation parameter was perfomed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"time_completed","description":"Datetime when the ventilation parameter was completed","dataType":{"label":"datetime","domainType":"PrimitiveType"}},{"label":"parameter_value_source","description":"Source results of the ventilaton parameter","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"parameter_value","description":"Translated values of the result if available","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"unit","description":"Units of the result","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"source_system","description":"Name of the system where data was sourced","dataType":{"label":"varchar","domainType":"PrimitiveType"}},{"label":"load_date","description":"Datetime when the data was loaded in the data warehouse","dataType":{"label":"datetime","domainType":"PrimitiveType"}}]}]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/6257e054b2f9517e2b479157.json b/src/controllers/datasetfiles/6257e054b2f9517e2b479157.json deleted file mode 100644 index 722c2233..00000000 --- a/src/controllers/datasetfiles/6257e054b2f9517e2b479157.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"Test emails - update","description":"lorem ipsum","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"Test emails - update"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"lorem ipsum"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"test@test.com"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Testing\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2022-04-21"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"IRREGULAR"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"VARIABLE"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"http://google.como\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"GB-ENG\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"lorem ipsum"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"OPCS4\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"HL7 FHIR\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"aa\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"image\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"measuredValue\":\"1000\",\"observationDate\":\"22/04/2022\",\"measuredProperty\":\"Count\",\"observedNode\":\"PERSONS\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f89662f7150a1b050be0710"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH AND SOCIAL CARE NORTHERN IRELAND"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"ALLIANCE"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/controllers/datasetfiles/dummy.json b/src/controllers/datasetfiles/dummy.json deleted file mode 100644 index c0fc228d..00000000 --- a/src/controllers/datasetfiles/dummy.json +++ /dev/null @@ -1 +0,0 @@ -{ "reasonForExistence": "I live so this folder will live" } From 45c265db5ac77c9eb33f9ba9acb75db1fa1e6ae8 Mon Sep 17 00:00:00 2001 From: kandaj Date: Wed, 24 Aug 2022 12:28:00 +0100 Subject: [PATCH 145/389] console.log removed --- src/resources/publisher/publisher.service.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/resources/publisher/publisher.service.js b/src/resources/publisher/publisher.service.js index a9b808dc..9f41f445 100644 --- a/src/resources/publisher/publisher.service.js +++ b/src/resources/publisher/publisher.service.js @@ -98,12 +98,6 @@ export default class PublisherService { async updateDataUseWidget(publisherId, content) { const publisher = await this.publisherRepository.getPublisher(publisherId); const data = { ...publisher.publisherDetails.dataUse.widget, ...content }; - - console.log('++++++'); - - console.log(data); - console.log('++++++'); - await this.publisherRepository.updateByQuery( { _id: publisherId }, { From 7709166abb5f1869327678438681922e65d0aa4f Mon Sep 17 00:00:00 2001 From: Geeta Date: Thu, 25 Aug 2022 11:34:46 +0100 Subject: [PATCH 146/389] added acrualPeriodicity --- src/resources/search/search.repository.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index 65ff6f20..e1d83252 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -313,6 +313,7 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, 'datasetv2.summary.publisher.name': 1, 'datasetv2.summary.publisher.logo': 1, 'datasetv2.summary.publisher.memberOf': 1, + 'datasetv2.provenance.temporal.accrualPeriodicity': 1, 'persons.id': 1, 'persons.firstname': 1, From faa034cd242ba575ea98f8f43842d29c5f9d95a9 Mon Sep 17 00:00:00 2001 From: Callum Reekie Date: Fri, 26 Aug 2022 10:01:59 +0100 Subject: [PATCH 147/389] Add dummy json files back in --- src/controllers/datasetfiles/dummy.json | 1 + src/resources/dataset/datasetfiles/61d462527fab6feaf7046bf7.json | 1 - src/resources/dataset/datasetfiles/61d6d831050b530d73689c96.json | 1 - src/resources/dataset/datasetfiles/61dffe2523edc44711e5a894.json | 1 - src/resources/dataset/datasetfiles/dummy.json | 1 + 5 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 src/controllers/datasetfiles/dummy.json delete mode 100644 src/resources/dataset/datasetfiles/61d462527fab6feaf7046bf7.json delete mode 100644 src/resources/dataset/datasetfiles/61d6d831050b530d73689c96.json delete mode 100644 src/resources/dataset/datasetfiles/61dffe2523edc44711e5a894.json create mode 100644 src/resources/dataset/datasetfiles/dummy.json diff --git a/src/controllers/datasetfiles/dummy.json b/src/controllers/datasetfiles/dummy.json new file mode 100644 index 00000000..c0fc228d --- /dev/null +++ b/src/controllers/datasetfiles/dummy.json @@ -0,0 +1 @@ +{ "reasonForExistence": "I live so this folder will live" } diff --git a/src/resources/dataset/datasetfiles/61d462527fab6feaf7046bf7.json b/src/resources/dataset/datasetfiles/61d462527fab6feaf7046bf7.json deleted file mode 100644 index da4f66ff..00000000 --- a/src/resources/dataset/datasetfiles/61d462527fab6feaf7046bf7.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"OTHER > HEALTH DATA RESEARCH U","description":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations\n\n![](https://raw.githubusercontent.com/HDRUK/papers/master/images/screenshot.png)\n\n","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"OTHER > HEALTH DATA RESEARCH U"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"susheel.varma@hdruk.ac.uk"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Preprints\",\"Papers\",\"HDR UK\"]"},{"namespace":"org.healthdatagateway","key":"properties/summary/doiName","value":"10.1093/ije/dyx196"},{"namespace":"org.healthdatagateway","key":"properties/documentation/description","value":"Publications that mention HDR-UK (or any variant thereof) in Acknowledgements or Author Affiliations\n\n![](https://raw.githubusercontent.com/HDRUK/papers/master/images/screenshot.png)\n\n"},{"namespace":"org.healthdatagateway","key":"properties/documentation/associatedMedia","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/documentation/isPartOf","value":"NOT APPLICABLE"},{"namespace":"org.healthdatagateway","key":"properties/coverage/spatial","value":"[\"https://www.geonames.org/countries/GB/united-kingdom.html\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/typicalAgeRange","value":"0-0"},{"namespace":"org.healthdatagateway","key":"properties/coverage/physicalSampleAvailability","value":"[\"NOT AVAILABLE\"]"},{"namespace":"org.healthdatagateway","key":"properties/coverage/followup","value":"UNKNOWN"},{"namespace":"org.healthdatagateway","key":"properties/coverage/pathway","value":"NOT APPLICABLE"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/purpose","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/source","value":"[\"MACHINE GENERATED\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/origin/collectionSituation","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"DAILY"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/distributionReleaseDate","value":"2020-11-27"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2020-03-31"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/endDate","value":"2022-04-30"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"NO TIMELAG"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseLimitation","value":"[\"GENERAL RESEARCH USE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/dataUseRequirements","value":"[\"RETURN TO DATABASE OR RESOURCE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/resourceCreator","value":"[\"HDR UK Science Team\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/investigations","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/usage/isReferencedBy","value":"[\"https://doi.org/10.5281/zenodo.326615\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"https://raw.githubusercontent.com/HDRUK/papers/master/LICENSE\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessService","value":"https://github.com/HDRUK/papers"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRequestCost","value":"[\"Free\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/deliveryLeadTime","value":"LESS 1 WEEK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"GB-ENG\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataProcessor","value":"HDR UK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"HDR UK"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"OTHER\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"en\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"CSV\",\"JSON\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/qualifiedRelation","value":"[\"https://web.www.healthdatagateway.org/dataset/fd8d0743-344a-4758-bb97-f8ad84a37357\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/derivation","value":"[\"https://web.www.healthdatagateway.org/dataset/fd8d0743-344a-4758-bb97-f8ad84a37357\"]"},{"namespace":"org.healthdatagateway","key":"properties/enrichmentAndLinkage/tools","value":"[\"https://github.com/HDRUK/papers\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"observedNode\":\"FINDINGS\",\"measuredValue\":575,\"disambiguatingDescription\":\"Number of papers with affiliation and/or acknowledgement to HDR UK\",\"observationDate\":\"27/11/2020\",\"measuredProperty\":\"Count\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[{"label":"papers","description":"HDR UK Paper and Preprints","childDataElements":[{"label":"urls","description":"List of URLS (DOI, HTML, PDF)","dataType":{"label":"List (URLS)","domainType":"PrimitiveType"}},{"label":"date","description":"Date of Publication","dataType":{"label":"Date","domainType":"PrimitiveType"}},{"label":"date","description":"Date of Publication1","dataType":{"label":"Date","domainType":"PrimitiveType"}}]}]}} \ No newline at end of file diff --git a/src/resources/dataset/datasetfiles/61d6d831050b530d73689c96.json b/src/resources/dataset/datasetfiles/61d6d831050b530d73689c96.json deleted file mode 100644 index ab9ae485..00000000 --- a/src/resources/dataset/datasetfiles/61d6d831050b530d73689c96.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"New dataset 6 Jan 2022 11:53","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"New dataset 6 Jan 2022 11:53"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f7b1a2bce9f65e6ed83e7da"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"HEALTH DATA RESEARCH UK"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"Other"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/resources/dataset/datasetfiles/61dffe2523edc44711e5a894.json b/src/resources/dataset/datasetfiles/61dffe2523edc44711e5a894.json deleted file mode 100644 index c5cf7f26..00000000 --- a/src/resources/dataset/datasetfiles/61dffe2523edc44711e5a894.json +++ /dev/null @@ -1 +0,0 @@ -{"dataModel":{"label":"New test dataset - changed","description":"This is a new test dataset - admin change","type":"Data Asset","metadata":[{"namespace":"org.healthdatagateway","key":"properties/summary/title","value":"New test dataset - changed"},{"namespace":"org.healthdatagateway","key":"properties/summary/abstract","value":"This is a new test dataset - admin change"},{"namespace":"org.healthdatagateway","key":"properties/summary/contactPoint","value":"test@test.com"},{"namespace":"org.healthdatagateway","key":"properties/summary/keywords","value":"[\"Testing\"]"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/startDate","value":"2022-01-20"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/timeLag","value":"LESS 1 WEEK"},{"namespace":"org.healthdatagateway","key":"properties/provenance/temporal/accrualPeriodicity","value":"IRREGULAR"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/accessRights","value":"[\"https://google.com\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/jurisdiction","value":"[\"AL\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/access/dataController","value":"Nunc nec semper erat. Quisque non eros a ipsum elementum imperdiet ac at leo. Phasellus et nunc sem. Curabitur laoreet ex nec neque maximus venenatis. Phasellus ullamcorper eu turpis a eleifend. Nulla scelerisque libero sem, quis fringilla lacus varius eu. Etiam venenatis, neque ut suscipit egestas, sem nisi imperdiet est, ut rutrum odio turpis eget nibh. Phasellus eget odio risus. Nam auctor eget nulla sit amet sodales. Maecenas blandit, odio quis vehicula ullamcorper, eros magna venenatis velit, vel consequat sapien enim at ex. Sed blandit suscipit odio, nec hendrerit quam luctus nec."},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/vocabularyEncodingScheme","value":"[\"LOCAL\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/conformsTo","value":"[\"HL7 FHIR\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/language","value":"[\"aa\"]"},{"namespace":"org.healthdatagateway","key":"properties/accessibility/formatAndStandards/format","value":"[\"image\"]"},{"namespace":"org.healthdatagateway","key":"properties/observations/observations","value":"[{\"measuredValue\":\"10000\",\"observationDate\":\"20/01/2022\",\"measuredProperty\":\"Count\",\"observedNode\":\"PERSONS\"}]"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/identifier","value":"5f3f98068af2ef61552e1d75"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/name","value":"SAIL"},{"namespace":"org.healthdatagateway","key":"properties/summary/publisher/memberOf","value":"ALLIANCE"}],"childDataClasses":[]}} \ No newline at end of file diff --git a/src/resources/dataset/datasetfiles/dummy.json b/src/resources/dataset/datasetfiles/dummy.json new file mode 100644 index 00000000..c0fc228d --- /dev/null +++ b/src/resources/dataset/datasetfiles/dummy.json @@ -0,0 +1 @@ +{ "reasonForExistence": "I live so this folder will live" } From f69f2129878719cf1f43280c02f6cf0482e6f5a7 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Wed, 31 Aug 2022 16:03:07 +0100 Subject: [PATCH 148/389] Adding condition --- .../datarequest/datarequest.controller.js | 6 +++++- src/resources/message/message.controller.js | 18 +++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/resources/datarequest/datarequest.controller.js b/src/resources/datarequest/datarequest.controller.js index d9c540b6..2f4cb631 100644 --- a/src/resources/datarequest/datarequest.controller.js +++ b/src/resources/datarequest/datarequest.controller.js @@ -464,7 +464,11 @@ export default class DataRequestController extends Controller { if (cacheEnabled) { let publisherDetails = await PublisherModel.findOne({ _id: ObjectId(accessRecord.publisherObj._id) }).lean(); - if (accessRecord.applicationStatus === constants.applicationStatuses.SUBMITTED && publisherDetails['dar-integration']['enabled']) { + if ( + accessRecord.applicationStatus === constants.applicationStatuses.SUBMITTED && + publisherDetails['dar-integration'] && + publisherDetails['dar-integration']['enabled'] + ) { const pubSubMessage = { id: '', type: '5safes', diff --git a/src/resources/message/message.controller.js b/src/resources/message/message.controller.js index 4ebcbb89..c8814598 100644 --- a/src/resources/message/message.controller.js +++ b/src/resources/message/message.controller.js @@ -41,7 +41,7 @@ module.exports = { }, }); - // 3. Return undefined if no object(s) exists + // 3. Return undefined if no object(s) exists if (_.isEmpty(tools)) return undefined; // 4. Get recipients for new message @@ -138,15 +138,16 @@ module.exports = { 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 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}]; + messageCreatorRecipient = [{ email: creatorEmail.email }]; messageRecipients = [...teamNotificationEmails, ...memberEmails]; } - } else { // only if not membersByType but has a team email setup messageRecipients = [...messageRecipients, ...teamNotificationEmails]; @@ -188,13 +189,14 @@ module.exports = { // publish the message to GCP PubSub 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']['enabled']) { + if (publisherDetails['dar-integration'] && publisherDetails['dar-integration']['enabled']) { const pubSubMessage = { - id: "", - type: "enquiry", + id: '', + type: 'enquiry', publisherInfo: { id: publisherDetails._id, name: publisherDetails.name, @@ -204,14 +206,12 @@ module.exports = { messageId: message.messageID, createdDate: message.createdDate, questionBank: req.body.firstMessage, - }, darIntegration: publisherDetails['dar-integration'], }; await publishMessageWithRetryToPubSub(process.env.PUBSUB_TOPIC_ENQUIRY, JSON.stringify(pubSubMessage)); } } - } // 19. Return successful response with message data const messageObj = message.toObject(); From f6068fbf951cfff4b5c6dc91960c451ce6f3794e Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 12:58:58 +0100 Subject: [PATCH 149/389] Create team fixes --- .../datarequest/datarequest.service.js | 1 + src/resources/team/team.controller.js | 31 ++++++++++--------- src/resources/utilities/cloudStorage.util.js | 22 +++++++++++-- src/services/datasetonboarding.service.js | 8 ++--- src/utils/datasetonboarding.util.js | 18 +++++------ 5 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 7f69530e..31b46173 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -441,6 +441,7 @@ export default class DataRequestService { // Process the files for scanning //lgtm [js/type-confusion-through-parameter-tampering] for (let i = 0; i < files.length; i++) { + console.log('files[i]', files[i]); // Get description information let description = descriptionArray ? descriptions[i] : descriptions; // Get uniqueId diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index f538b5e1..cdcbd495 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -693,6 +693,7 @@ const addTeam = async (req, res) => { publisher.name = `${inputSanitizer.removeNonBreakingSpaces(memberOf)} > ${inputSanitizer.removeNonBreakingSpaces(name)}`; publisher.publisherDetails = { + ...publisher.publisherDetails, name: inputSanitizer.removeNonBreakingSpaces(name), memberOf: inputSanitizer.removeNonBreakingSpaces(memberOf), contactPoint: inputSanitizer.removeNonBreakingSpaces(contactPoint), @@ -722,7 +723,7 @@ const addTeam = async (req, res) => { // 11. Send email and notification to managers await createNotifications(constants.notificationTypes.TEAMADDED, { recipients }, name, req.user, publisherId); - return res.status(200).json({ success: true }); + return res.status(200).json(newPublisher); } catch (err) { console.error(err.message); return res.status(500).json({ @@ -971,21 +972,23 @@ const checkIfAdmin = (user, adminRoles) => { const getTeamMembersByRole = (team, role) => { let { members = [], users = [] } = team; - let userIds = members.filter(mem => { - if (mem.roles.includes(role) || (role === 'All' && _.has(mem, 'roles'))) { - if(!_.has(mem, 'notifications')) { - return true; - } + let userIds = members + .filter(mem => { + if (mem.roles.includes(role) || (role === 'All' && _.has(mem, 'roles'))) { + if (!_.has(mem, 'notifications')) { + return true; + } - if (_.has(mem, 'notifications') && mem.notifications.length === 0) { - return true; - } - - if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { - return true; + if (_.has(mem, 'notifications') && mem.notifications.length === 0) { + return true; + } + + if (_.has(mem, 'notifications') && mem.notifications.length && mem.notifications[0].optIn) { + return true; + } } - } - }).map(mem => mem.memberid.toString()); + }) + .map(mem => mem.memberid.toString()); return users.filter(user => userIds.includes(user._id.toString())); }; diff --git a/src/resources/utilities/cloudStorage.util.js b/src/resources/utilities/cloudStorage.util.js index e85a1267..b6ff1b80 100644 --- a/src/resources/utilities/cloudStorage.util.js +++ b/src/resources/utilities/cloudStorage.util.js @@ -7,13 +7,21 @@ export const fileStatus = { UPLOADED: 'UPLOADED', ERROR: 'ERROR', SCANNED: 'SCANNED', - QUARANTINED: 'QUARANTINED' + QUARANTINED: 'QUARANTINED', }; export const processFile = (file, id, uniqueId) => new Promise(async (resolve, reject) => { - const storage = new Storage(); + const storage = new Storage({ + projectId: 'hdruk-gateway-dev', + credentials: { + client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', + private_key: + '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', + }, + }); let { originalname, path } = file; + storage.bucket(bucketName).upload( path, { @@ -28,6 +36,7 @@ export const processFile = (file, id, uniqueId) => // resolve resolve({ status: fileStatus.UPLOADED, file }); } else { + console.log('err', err); resolve({ status: fileStatus.ERROR, file }); } } @@ -37,7 +46,14 @@ export const processFile = (file, id, uniqueId) => export const getFile = (file, fileId, id) => new Promise(async resolve => { // 1. new storage obj - const storage = new Storage(); + const storage = new Storage({ + projectId: 'hdruk-gateway-dev', + credentials: { + client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', + private_key: + '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', + }, + }); // 2. set option for file dest let options = { // The path to which the file should be downloaded diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index 54580148..1bb52922 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -221,12 +221,8 @@ export default class DatasetOnboardingService { getDatasetVersion = async id => { let dataset = await Data.findOne({ _id: id }); - if (dataset.questionAnswers) { - dataset.questionAnswers = JSON.parse(dataset.questionAnswers); - } else { - dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); - await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); - } + dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); + await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); if (_.isEmpty(dataset.structuralMetadata)) { dataset.structuralMetadata = datasetonboardingUtil.populateStructuralMetadata(dataset.datasetfields.technicaldetails); diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 75ed7674..01d676ed 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -884,8 +884,10 @@ const createNotifications = async (type, context) => { team = await TeamModel.findOne({ _id: context.datasetv2.summary.publisher.identifier }).lean(); for (let member of team.members) { - if ((Array.isArray(member.roles) && member.roles.some(role => ['manager', 'metadata_editor'].includes(role))) - || (typeof member.roles === 'string' && ['manager', 'metadata_editor'].includes(member.roles))) { + 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); } } @@ -958,14 +960,10 @@ const createNotifications = async (type, context) => { }; html = emailGenerator.generateMetadataOnboardingRejected(options); - let subject = (options.isFederated) ? 'Your federated dataset has been rejected and requires review' : 'Your dataset version has been reviewed and rejected'; - emailGenerator.sendEmail( - teamMembersDetails, - constants.hdrukEmail, - subject, - html, - false - ); + let subject = options.isFederated + ? 'Your federated dataset has been rejected and requires review' + : 'Your dataset version has been reviewed and rejected'; + emailGenerator.sendEmail(teamMembersDetails, constants.hdrukEmail, subject, html, false); break; case constants.notificationTypes.DATASETDUPLICATED: // 1. Get user removed From 9b8a94c2e8351f0dfbdbcd8d34bf1ac54a106389 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:18:24 +0100 Subject: [PATCH 150/389] Revert cloud storage file --- src/resources/utilities/cloudStorage.util.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/resources/utilities/cloudStorage.util.js b/src/resources/utilities/cloudStorage.util.js index b6ff1b80..31c9d4e2 100644 --- a/src/resources/utilities/cloudStorage.util.js +++ b/src/resources/utilities/cloudStorage.util.js @@ -12,16 +12,8 @@ export const fileStatus = { export const processFile = (file, id, uniqueId) => new Promise(async (resolve, reject) => { - const storage = new Storage({ - projectId: 'hdruk-gateway-dev', - credentials: { - client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', - private_key: - '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', - }, - }); + const storage = new Storage(); let { originalname, path } = file; - storage.bucket(bucketName).upload( path, { @@ -36,7 +28,6 @@ export const processFile = (file, id, uniqueId) => // resolve resolve({ status: fileStatus.UPLOADED, file }); } else { - console.log('err', err); resolve({ status: fileStatus.ERROR, file }); } } @@ -46,14 +37,7 @@ export const processFile = (file, id, uniqueId) => export const getFile = (file, fileId, id) => new Promise(async resolve => { // 1. new storage obj - const storage = new Storage({ - projectId: 'hdruk-gateway-dev', - credentials: { - client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', - private_key: - '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', - }, - }); + const storage = new Storage(); // 2. set option for file dest let options = { // The path to which the file should be downloaded From 8284a2bf7457cd693e53935ab60532aaf3227898 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:20:38 +0100 Subject: [PATCH 151/389] Incorrect changes --- src/services/datasetonboarding.service.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index 1bb52922..54580148 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -221,8 +221,12 @@ export default class DatasetOnboardingService { getDatasetVersion = async id => { let dataset = await Data.findOne({ _id: id }); - dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); - await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); + if (dataset.questionAnswers) { + dataset.questionAnswers = JSON.parse(dataset.questionAnswers); + } else { + dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); + await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); + } if (_.isEmpty(dataset.structuralMetadata)) { dataset.structuralMetadata = datasetonboardingUtil.populateStructuralMetadata(dataset.datasetfields.technicaldetails); From 97e374d0e7572521d50c8eae2647b142289a7501 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:21:25 +0100 Subject: [PATCH 152/389] Remove console log --- src/resources/datarequest/datarequest.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 31b46173..7f69530e 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -441,7 +441,6 @@ export default class DataRequestService { // Process the files for scanning //lgtm [js/type-confusion-through-parameter-tampering] for (let i = 0; i < files.length; i++) { - console.log('files[i]', files[i]); // Get description information let description = descriptionArray ? descriptions[i] : descriptions; // Get uniqueId From 031d5914c59d650064b7f0ec6b7fed7319f838d9 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 12:58:58 +0100 Subject: [PATCH 153/389] Create team fixes --- .../datarequest/datarequest.service.js | 1 + src/resources/team/team.controller.js | 3 ++- src/resources/utilities/cloudStorage.util.js | 22 ++++++++++++++++--- src/services/datasetonboarding.service.js | 8 ++----- src/utils/datasetonboarding.util.js | 18 +++++++-------- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 7f69530e..31b46173 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -441,6 +441,7 @@ export default class DataRequestService { // Process the files for scanning //lgtm [js/type-confusion-through-parameter-tampering] for (let i = 0; i < files.length; i++) { + console.log('files[i]', files[i]); // Get description information let description = descriptionArray ? descriptions[i] : descriptions; // Get uniqueId diff --git a/src/resources/team/team.controller.js b/src/resources/team/team.controller.js index 12c03d47..87c925a3 100644 --- a/src/resources/team/team.controller.js +++ b/src/resources/team/team.controller.js @@ -694,6 +694,7 @@ const addTeam = async (req, res) => { publisher.name = `${inputSanitizer.removeNonBreakingSpaces(memberOf)} > ${inputSanitizer.removeNonBreakingSpaces(name)}`; publisher.publisherDetails = { + ...publisher.publisherDetails, name: inputSanitizer.removeNonBreakingSpaces(name), memberOf: inputSanitizer.removeNonBreakingSpaces(memberOf), contactPoint: inputSanitizer.removeNonBreakingSpaces(contactPoint), @@ -723,7 +724,7 @@ const addTeam = async (req, res) => { // 11. Send email and notification to managers await createNotifications(constants.notificationTypes.TEAMADDED, { recipients }, name, req.user, publisherId); - return res.status(200).json({ success: true }); + return res.status(200).json(newPublisher); } catch (err) { console.error(err.message); return res.status(500).json({ diff --git a/src/resources/utilities/cloudStorage.util.js b/src/resources/utilities/cloudStorage.util.js index e85a1267..b6ff1b80 100644 --- a/src/resources/utilities/cloudStorage.util.js +++ b/src/resources/utilities/cloudStorage.util.js @@ -7,13 +7,21 @@ export const fileStatus = { UPLOADED: 'UPLOADED', ERROR: 'ERROR', SCANNED: 'SCANNED', - QUARANTINED: 'QUARANTINED' + QUARANTINED: 'QUARANTINED', }; export const processFile = (file, id, uniqueId) => new Promise(async (resolve, reject) => { - const storage = new Storage(); + const storage = new Storage({ + projectId: 'hdruk-gateway-dev', + credentials: { + client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', + private_key: + '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', + }, + }); let { originalname, path } = file; + storage.bucket(bucketName).upload( path, { @@ -28,6 +36,7 @@ export const processFile = (file, id, uniqueId) => // resolve resolve({ status: fileStatus.UPLOADED, file }); } else { + console.log('err', err); resolve({ status: fileStatus.ERROR, file }); } } @@ -37,7 +46,14 @@ export const processFile = (file, id, uniqueId) => export const getFile = (file, fileId, id) => new Promise(async resolve => { // 1. new storage obj - const storage = new Storage(); + const storage = new Storage({ + projectId: 'hdruk-gateway-dev', + credentials: { + client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', + private_key: + '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', + }, + }); // 2. set option for file dest let options = { // The path to which the file should be downloaded diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index 54580148..1bb52922 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -221,12 +221,8 @@ export default class DatasetOnboardingService { getDatasetVersion = async id => { let dataset = await Data.findOne({ _id: id }); - if (dataset.questionAnswers) { - dataset.questionAnswers = JSON.parse(dataset.questionAnswers); - } else { - dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); - await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); - } + dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); + await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); if (_.isEmpty(dataset.structuralMetadata)) { dataset.structuralMetadata = datasetonboardingUtil.populateStructuralMetadata(dataset.datasetfields.technicaldetails); diff --git a/src/utils/datasetonboarding.util.js b/src/utils/datasetonboarding.util.js index 75ed7674..01d676ed 100644 --- a/src/utils/datasetonboarding.util.js +++ b/src/utils/datasetonboarding.util.js @@ -884,8 +884,10 @@ const createNotifications = async (type, context) => { team = await TeamModel.findOne({ _id: context.datasetv2.summary.publisher.identifier }).lean(); for (let member of team.members) { - if ((Array.isArray(member.roles) && member.roles.some(role => ['manager', 'metadata_editor'].includes(role))) - || (typeof member.roles === 'string' && ['manager', 'metadata_editor'].includes(member.roles))) { + 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); } } @@ -958,14 +960,10 @@ const createNotifications = async (type, context) => { }; html = emailGenerator.generateMetadataOnboardingRejected(options); - let subject = (options.isFederated) ? 'Your federated dataset has been rejected and requires review' : 'Your dataset version has been reviewed and rejected'; - emailGenerator.sendEmail( - teamMembersDetails, - constants.hdrukEmail, - subject, - html, - false - ); + let subject = options.isFederated + ? 'Your federated dataset has been rejected and requires review' + : 'Your dataset version has been reviewed and rejected'; + emailGenerator.sendEmail(teamMembersDetails, constants.hdrukEmail, subject, html, false); break; case constants.notificationTypes.DATASETDUPLICATED: // 1. Get user removed From 6d4990b36ce581e68bf48f62e105e9fd984cce39 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:18:24 +0100 Subject: [PATCH 154/389] Revert cloud storage file --- src/resources/utilities/cloudStorage.util.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/resources/utilities/cloudStorage.util.js b/src/resources/utilities/cloudStorage.util.js index b6ff1b80..31c9d4e2 100644 --- a/src/resources/utilities/cloudStorage.util.js +++ b/src/resources/utilities/cloudStorage.util.js @@ -12,16 +12,8 @@ export const fileStatus = { export const processFile = (file, id, uniqueId) => new Promise(async (resolve, reject) => { - const storage = new Storage({ - projectId: 'hdruk-gateway-dev', - credentials: { - client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', - private_key: - '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', - }, - }); + const storage = new Storage(); let { originalname, path } = file; - storage.bucket(bucketName).upload( path, { @@ -36,7 +28,6 @@ export const processFile = (file, id, uniqueId) => // resolve resolve({ status: fileStatus.UPLOADED, file }); } else { - console.log('err', err); resolve({ status: fileStatus.ERROR, file }); } } @@ -46,14 +37,7 @@ export const processFile = (file, id, uniqueId) => export const getFile = (file, fileId, id) => new Promise(async resolve => { // 1. new storage obj - const storage = new Storage({ - projectId: 'hdruk-gateway-dev', - credentials: { - client_email: 'fma-local-secrets-access@hdruk-gateway-dev.iam.gserviceaccount.com', - private_key: - '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+y8zcTjzV6zA\njhJQ7HxFFXuD2iZz+SYtIQs1woA7HApv3EMRM4b38xwdYU242kCACym09B/BJG1w\nRiJNDU4wemKHAZyw2+lfwQd+DCPmwWO/BRXdJfCSegG96kVKmLRCwk3a08wKXNkX\n+UAW30bgM7q39RszB6gVOh92mEBEWzGZpqR/fBLMdW1UZw4AoOXvpurFaMLIfnu0\nNURNktYX+tKtOIhvZaLVl4hk30B/z6ceE/2tUXtmZBR4779q3YmfWAtoMkqNli5c\noEvoj+QJ6LjhUJ46rXAwpcQ+jkxY+Pqxa6FgHT3UP2/n1vmucjGPEQ/bCj2MRHK1\nrTkzBcTXAgMBAAECggEAJOOw4ubE2rmkYIIxSB06naVgiXhFP/42oSABCVqLPr6x\nZH0ZM+hoGKF/t0xBGSf5eGUGf58SFmf20Qg3FJuisTLQ/CLt3uRgPPCV/q9Ov0mu\n1oOSMqBL0UNYHVW7cFMp90eibaHtjFj0/rwMQvPtE2U11lxfscf+1CXFKl7dY9Jn\nrwchIRKadK1MU31BL9jcA9TapOlpLWAw7IVzMDLW7fexSZ0BdNlsWCuamRN7mR37\nHQpf60oNonUPHJDZxBiaawxIZCcPxazCVGgEvANlmUbAYtvbIQNzNi/iPokklGr5\nG9i8Sb1smQSPs1sNcF2iYwrlTpLVXHs7XBQJILy37QKBgQDTfa/xdtY9EWnfeoIr\n7A3d/dJszpxvY43BjDSL0uE2VaRg4BkT6lcndeQIZINzfN/gfQ35QDO6hL1VIjw0\nHRmNHPsUy2yNaeMphR6IHeb/93fbHFCaxIt3rDUJ0AWBUI4sQ9az+gSlxuHoUP72\ncojzSpWH8BFAzN2sriRJWPGphQKBgQC1i5bF9hAYxwQCqYCQyC0bQuYOp7dGCJOH\nywLor1MGSNPGwP4mmZvnEGPuFJMGjFNC3F1PWh1Vc58+C9xkzNm6TiZfPW/syazX\nSpgBJBPp7fSt4KE7zYC9RxCx0UE82Iq8R0tQolCePSpxlj6FtXdQaMBKshNHq8lW\nAVGqms01qwKBgHqy4s7OSUEoxynWo4rj6q+uQNTLkcq2EVoHA71TXUHuse1aVnJN\ndL76EJAl8HWiLyjIpMDE1L4ToNyxfggmEdriw3CvDI2Grzo5dxs1bf13D1xZsxH3\nkYqeqC7HN0ps9+8KdJf6J5QeLJZh12oXzQiJbvNs/jWAJeO3LGo5ZgsZAoGBAJH1\n19PzYL+7CoNlakN9XZ6ka7juqRB6hrrPwtf3VgYgeXw74FqCd/EPoXhiPE8wdSS1\nbaQF7L2uRyKogJrehFMg0AMUry8uenNhutJ4Aja7JVTC3H7rW41SXJ7Kgev8oZZ8\nr5X12mFPBhfq1vvDiVkxzyB/Cbc+65HlX5aN/V0fAoGAd3ylo4DIFUofFOM+/vb4\npWYyhddemfuZqOIPFRjXkqsN1rm4JdI14Cs7yxFan8WvENPkAL4dxzj4GOShZ+Qm\n3Z9qz1HNY766RBgv0hiFcxKOSQrJCbW0CGdtTyrXruNkM0z1cTotAl4tgB77XeVT\nbRhqFGhcUGG5YWP4ZTeEhrU=\n-----END PRIVATE KEY-----\n', - }, - }); + const storage = new Storage(); // 2. set option for file dest let options = { // The path to which the file should be downloaded From 8af55f1754cc2f0f1178d88c843b3908cac71bf7 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:20:38 +0100 Subject: [PATCH 155/389] Incorrect changes --- src/services/datasetonboarding.service.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/services/datasetonboarding.service.js b/src/services/datasetonboarding.service.js index 1bb52922..54580148 100644 --- a/src/services/datasetonboarding.service.js +++ b/src/services/datasetonboarding.service.js @@ -221,8 +221,12 @@ export default class DatasetOnboardingService { getDatasetVersion = async id => { let dataset = await Data.findOne({ _id: id }); - dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); - await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); + if (dataset.questionAnswers) { + dataset.questionAnswers = JSON.parse(dataset.questionAnswers); + } else { + dataset.questionAnswers = datasetonboardingUtil.populateQuestionAnswers(dataset.datasetv2); + await this.datasetOnboardingRepository.updateByQuery({ _id: id }, { questionAnswers: JSON.stringify(dataset.questionAnswers) }); + } if (_.isEmpty(dataset.structuralMetadata)) { dataset.structuralMetadata = datasetonboardingUtil.populateStructuralMetadata(dataset.datasetfields.technicaldetails); From 8af4f6273dba4b76a91632dd3298a2c9ba59fcd7 Mon Sep 17 00:00:00 2001 From: Peter Hammans Date: Fri, 2 Sep 2022 14:21:25 +0100 Subject: [PATCH 156/389] Remove console log --- src/resources/datarequest/datarequest.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/datarequest/datarequest.service.js b/src/resources/datarequest/datarequest.service.js index 31b46173..7f69530e 100644 --- a/src/resources/datarequest/datarequest.service.js +++ b/src/resources/datarequest/datarequest.service.js @@ -441,7 +441,6 @@ export default class DataRequestService { // Process the files for scanning //lgtm [js/type-confusion-through-parameter-tampering] for (let i = 0; i < files.length; i++) { - console.log('files[i]', files[i]); // Get description information let description = descriptionArray ? descriptions[i] : descriptions; // Get uniqueId From 5363350cc24bedc82e526348728924cfde9d9aa5 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Mon, 5 Sep 2022 14:50:20 +0100 Subject: [PATCH 157/389] Returning additional prop --- src/resources/search/search.repository.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/search/search.repository.js b/src/resources/search/search.repository.js index e1d83252..5f6a78f5 100644 --- a/src/resources/search/search.repository.js +++ b/src/resources/search/search.repository.js @@ -310,10 +310,11 @@ export async function getObjectResult(type, searchAll, searchQuery, startIndex, 'datasetfields.abstract': 1, 'datasetfields.ageBand': 1, 'datasetfields.phenotypes': 1, + 'datasetv2.accessibility.access.deliveryLeadTime': 1, 'datasetv2.summary.publisher.name': 1, 'datasetv2.summary.publisher.logo': 1, 'datasetv2.summary.publisher.memberOf': 1, - 'datasetv2.provenance.temporal.accrualPeriodicity': 1, + 'datasetv2.provenance.temporal.accrualPeriodicity': 1, 'persons.id': 1, 'persons.firstname': 1, From d2ae91f5df5c04a13ceab6c379ec628367baa5d5 Mon Sep 17 00:00:00 2001 From: kymmeh Date: Wed, 14 Sep 2022 17:18:26 +0100 Subject: [PATCH 158/389] Adding hdruk domain to CORS --- 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 e7ba46aa..cbdb1bbd 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -53,7 +53,7 @@ configuration.findAccount = Account.findAccount; const oidc = new Provider(process.env.api_url || 'http://localhost:3001', configuration); oidc.proxy = true; -var domains = [/\.healthdatagateway\.org$/, process.env.homeURL]; +var domains = [/\.healthdatagateway\.org$/, /\.hdruk\.dev$/, process.env.homeURL]; var rx = /^((http|https)+:\/\/[a-z]+)\.([^/]*)/; var arr = rx.exec(process.env.homeURL); From 091d24ea4ea1e5f39484497266b8eefb61229daa Mon Sep 17 00:00:00 2001 From: kymmeh Date: Wed, 14 Sep 2022 17:18:26 +0100 Subject: [PATCH 159/389] Adding hdruk domain to CORS --- 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 e7ba46aa..cbdb1bbd 100644 --- a/src/config/server.js +++ b/src/config/server.js @@ -53,7 +53,7 @@ configuration.findAccount = Account.findAccount; const oidc = new Provider(process.env.api_url || 'http://localhost:3001', configuration); oidc.proxy = true; -var domains = [/\.healthdatagateway\.org$/, process.env.homeURL]; +var domains = [/\.healthdatagateway\.org$/, /\.hdruk\.dev$/, process.env.homeURL]; var rx = /^((http|https)+:\/\/[a-z]+)\.([^/]*)/; var arr = rx.exec(process.env.homeURL); From 6b47ae3ebd967280b93bd465d6e21c063f65d3d0 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 28 Sep 2022 16:29:58 +0100 Subject: [PATCH 160/389] 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 161/389] 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 162/389] 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 163/389] 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 164/389] 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 165/389] 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 166/389] 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 167/389] 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 168/389] 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 169/389] 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 170/389] 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 171/389] 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 172/389] 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 173/389] 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 174/389] 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 175/389] 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 176/389] 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 = `
+
+
- A new Workflow has been ${action}. + A ${action == 'created' ? 'new' : ''} Workflow has been ${action}.
- ${actioner} has ${action} ${workflowName} on ${moment(createdAt).format('D MMM YYYY')} + A data access request ${workflowName} workflow has been ${action} by ${actioner} on ${moment(currentDate).format( + 'D MMM YYYY' + )}
+ + + + + + + + + + + + + +
+ ${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 177/389] 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 178/389] 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 179/389] 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 180/389] 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 181/389] 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 182/389] 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 183/389] 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 184/389] 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 185/389] 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 186/389] 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 187/389] 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 188/389] 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 189/389] 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 190/389] 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 191/389] 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 192/389] 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 193/389] 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 194/389] 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 195/389] 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 196/389] 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 197/389] 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 198/389] 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 199/389] 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 200/389] 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 201/389] 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 202/389] 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 203/389] 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 204/389] 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 205/389] 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 206/389] 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 207/389] 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 208/389] 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 209/389] 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 210/389] 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 211/389] 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 212/389] 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 213/389] 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 214/389] 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 215/389] 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 216/389] 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 217/389] 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 218/389] 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 219/389] 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 220/389] 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 221/389] 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 222/389] 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 223/389] 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 224/389] 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 225/389] 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 226/389] 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 227/389] 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 228/389] 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 229/389] 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 230/389] 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 231/389] 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 232/389] 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 233/389] 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 234/389] 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 235/389] 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 236/389] 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 237/389] 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 238/389] 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 239/389] 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 240/389] 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 241/389] 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 242/389] 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 243/389] 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 244/389] 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 245/389] 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 246/389] 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 247/389] 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 248/389] 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 249/389] 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 250/389] 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 251/389] 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 252/389] 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 253/389] 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 254/389] 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 255/389] 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 256/389] 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 257/389] 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 258/389] 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 259/389] 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 260/389] 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 261/389] 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 262/389] 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 263/389] 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 264/389] 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 265/389] 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 266/389] 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 267/389] 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 268/389] 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 269/389] 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 270/389] 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 271/389] 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 272/389] 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 273/389] 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 274/389] 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 275/389] 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 276/389] 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 277/389] 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 278/389] 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 279/389] 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 280/389] 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 281/389] 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 282/389] 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 283/389] 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 284/389] 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 285/389] 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 286/389] 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 287/389] 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 288/389] 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 289/389] 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 290/389] 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 291/389] 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 292/389] 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 293/389] 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 294/389] 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 295/389] 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 296/389] 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 297/389] 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 298/389] 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 299/389] 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 300/389] 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 301/389] 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 302/389] 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 303/389] 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 304/389] 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 305/389] 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 306/389] 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 307/389] 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 308/389] 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 309/389] 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 310/389] 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 311/389] 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 312/389] 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 313/389] 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 314/389] 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 315/389] 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 316/389] 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 317/389] 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 318/389] 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 319/389] 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 320/389] 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 321/389] 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 322/389] 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 323/389] 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 324/389] 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 325/389] 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 326/389] 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 327/389] 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 328/389] 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 329/389] 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 330/389] 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 331/389] 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 332/389] 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 333/389] 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 334/389] 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 335/389] 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 336/389] 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 337/389] 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 338/389] 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 339/389] 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 340/389] 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 341/389] 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 342/389] 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 343/389] 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 344/389] 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 345/389] 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 346/389] 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 347/389] 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 348/389] 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 349/389] 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 350/389] 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 351/389] 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 352/389] 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 353/389] 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 354/389] 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 355/389] 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 356/389] 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 357/389] 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 358/389] 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 359/389] 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 360/389] 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 361/389] 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 362/389] 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 363/389] 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 364/389] 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 365/389] 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 366/389] 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 367/389] 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 368/389] 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 369/389] 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 370/389] 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 371/389] 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 372/389] 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 373/389] 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 374/389] 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 375/389] 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 376/389] 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 379/389] 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 380/389] 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 381/389] 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 382/389] 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 383/389] 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 384/389] 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 385/389] 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 386/389] 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 387/389] 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 388/389] 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 From ddb4cdbec32a9c844dc68f267459fa7c1d22d936 Mon Sep 17 00:00:00 2001 From: Dan Nita Date: Wed, 6 Nov 2024 12:52:52 +0000 Subject: [PATCH 389/389] GAT-5664 :: www to old --- src/resources/dataset/__mocks__/datasets.js | 4 ++-- src/resources/dataset/dataset.entity.js | 2 +- src/resources/project/__mocks__/projects.js | 2 +- src/resources/utilities/ga4gh.utils.js | 2 +- src/utils/schema.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/resources/dataset/__mocks__/datasets.js b/src/resources/dataset/__mocks__/datasets.js index c1e9f4ef..4dacb5c0 100644 --- a/src/resources/dataset/__mocks__/datasets.js +++ b/src/resources/dataset/__mocks__/datasets.js @@ -205,7 +205,7 @@ export const v2DatasetsStub = [ pid: 'b67f0edd-fed2-4d68-a25f-d225759aa3b0', id: 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', version: '0.0.1', - identifier: 'https://web.www.healthdatagateway.org/dataset/dfb21b3b-7fd9-40c4-892e-810edd6dfc25', + identifier: 'https://web.old.healthdatagateway.org/dataset/dfb21b3b-7fd9-40c4-892e-810edd6dfc25', revisions: { '0.0.1': 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', latest: 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', @@ -252,7 +252,7 @@ export const v2DatasetsStub = [ pid: 'b67f0edd-fed2-4d68-a25f-d225759aa3b0', id: 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', version: '0.0.1', - identifier: 'https://web.www.healthdatagateway.org/dataset/dfb21b3b-7fd9-40c4-892e-810edd6dfc25', + identifier: 'https://web.old.healthdatagateway.org/dataset/dfb21b3b-7fd9-40c4-892e-810edd6dfc25', revisions: { '0.0.1': 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', latest: 'dfb21b3b-7fd9-40c4-892e-810edd6dfc25', diff --git a/src/resources/dataset/dataset.entity.js b/src/resources/dataset/dataset.entity.js index 2e00900d..cd262fc9 100644 --- a/src/resources/dataset/dataset.entity.js +++ b/src/resources/dataset/dataset.entity.js @@ -44,7 +44,7 @@ export default class DatasetClass extends Entity { const transformedObject = this.transformTo(transformer, { strict: false }); // Manually update identifier URL link - transformedObject.dataset.identifier = `https://web.www.healthdatagateway.org/dataset/${this.datasetid}`; + transformedObject.dataset.identifier = `https://web.old.healthdatagateway.org/dataset/${this.datasetid}`; // Append static schema details for v2 transformedObject.dataset['@schema'] = { diff --git a/src/resources/project/__mocks__/projects.js b/src/resources/project/__mocks__/projects.js index 91006330..8f06d30e 100644 --- a/src/resources/project/__mocks__/projects.js +++ b/src/resources/project/__mocks__/projects.js @@ -186,7 +186,7 @@ export const projectsStub = [ "id": 7635028705590892, "type": "project", "name": "How can NCS healthcare data be connected with wastewater surveillance of COVID-19 in a privacy-preserving fashion to inform epidemiological models and democratise data access?", - "link": "https://web.www.healthdatagateway.org/project/7635028705590892", + "link": "https://web.old.healthdatagateway.org/project/7635028705590892", "description": "Wastewater-based epidemiology (WBE), i.e. the monitoring of public health using samples collected from sewerage systems, offers the unique opportunity to make healthcare data widely available without compromising the privacy of individuals: by their very nature, sewerage systems aggregate the signal from thousands of people, diminishing the ability to identify any one individual. In addition to ongoing work modelling the relationship between case numbers and wastewater data, this project will deliver five additional benefits:\n* provide data on COVID-19 related hospital admissions and symptoms as a complementary signal to refine WBE modelling with higher spatiotemporal resolution than is currently available.\n* develop standards for exchanging WBE data that support automatic validation, increasing our confidence in the data.\nprovide consistent methods to aggregate healthcare data to catchment areas across England, Scotland, and Wales.\n* develop sound, privacy-preserving aggregation methods for sensitive data in the context of WBE that could help address future public health concerns, e.g. influenza outbreaks or illicit drug consumption.\n* subject to privacy due diligence, generate data products that can be shared publicly, aligning with the government’s open data strategy and allowing a broader audience to participate in WBE.\n", "resultsInsights": "", "activeflag": "active", diff --git a/src/resources/utilities/ga4gh.utils.js b/src/resources/utilities/ga4gh.utils.js index 739c5bea..0cbf507b 100644 --- a/src/resources/utilities/ga4gh.utils.js +++ b/src/resources/utilities/ga4gh.utils.js @@ -69,7 +69,7 @@ const _buildGa4ghVisas = async user => { type: 'ControlledAccessGrants', asserted: dar.dateFinalStatus.getTime(), //date DAR was approved value: dar.pids.map(pid => { - return 'https://web.www.healthdatagateway.org/dataset/' + pid; + return 'https://web.old.healthdatagateway.org/dataset/' + pid; }), //URL to each dataset that they have been approved for source: 'https://www.healthdatagateway.org', by: 'dac', diff --git a/src/utils/schema.json b/src/utils/schema.json index 4db80758..470c9384 100644 --- a/src/utils/schema.json +++ b/src/utils/schema.json @@ -13,7 +13,7 @@ "title": "Dataset identifier", "$comment": "http://purl.org/dc/terms/identifier", "examples": [ - ["226fb3f1-4471-400a-8c39-2b66d46a39b6", "https://web.www.healthdatagateway.org/dataset/226fb3f1-4471-400a-8c39-2b66d46a39b6"] + ["226fb3f1-4471-400a-8c39-2b66d46a39b6", "https://web.old.healthdatagateway.org/dataset/226fb3f1-4471-400a-8c39-2b66d46a39b6"] ], "description": "System dataset identifier", "anyOf": [
@@ -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 377/389] 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 378/389] 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 + +