Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"ignoreRoot": [
".git",
".nyc_output",
".sass-cache",
"bower_components",
"coverage",
"./node_modules/!(q-templates-application)/dist/template.json"
],
"watch": [
"node_modules/q-templates-application/dist/template.json",
"db",
"docs",
"middleware",
"openapi",
"public",
"questionnaire",
"services",
"app.js"
]
}
20 changes: 17 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
},
"scripts": {
"start": "node ./bin/www",
"start:dev": "nodemon -L --inspect=0.0.0.0:9229 -e .js,.json,.njk,.yml --ignore openapi/openapi.json --exec npm run build:run",
"start:dev": "nodemon -L -e .js,.json,.njk,.yml --ignore openapi/openapi.json --exec npm run build:run:dev",
"openapi:build": "speccy lint openapi/src/openapi-src.json -j && speccy resolve ./openapi/src/openapi-src.json -j | yaml2json --pretty --indentation 4 --save - > ./openapi/openapi.json && node ./openapi/src/dereference-openapi.js",
"build:run": "npm run openapi:build && npm run start",
"build:run:dev": "npm run openapi:build && node --inspect=0.0.0.0:9229 ./bin/www",
"pretestx": "eslint .",
"test": "jest",
"coverage": "jest --coverage",
Expand Down Expand Up @@ -40,8 +40,9 @@
"notifications-node-client": "^5.1.0",
"pg": "^8.7.3",
"pino-http": "^5.5.0",
"q-expressions": "github:CriminalInjuriesCompensationAuthority/q-expressions#v1.0.0",
"q-router": "github:CriminalInjuriesCompensationAuthority/q-router#v3.0.0",
"q-templates-application": "github:CriminalInjuriesCompensationAuthority/q-templates-application#v8.0.3",
"q-templates-application": "github:CriminalInjuriesCompensationAuthority/q-templates-application#rc-personalised-notifications",
"swagger-ui-express": "^4.3.0",
"uuid": "^3.3.2",
"verror": "^1.10.0"
Expand Down
15 changes: 6 additions & 9 deletions questionnaire/dataset/dataset-service.declaration.test.js

Large diffs are not rendered by default.

307 changes: 0 additions & 307 deletions questionnaire/notifications.test.js

This file was deleted.

75 changes: 26 additions & 49 deletions questionnaire/questionnaire-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ const VError = require('verror');
const createQRouter = require('q-router');
const uuidv4 = require('uuid/v4');
const ajvFormatsMobileUk = require('ajv-formats-mobile-uk');
const JsonTranslator = require('json-translator');
const templates = require('./templates');
const createMessageBusCaller = require('../services/message-bus');
const createNotifyService = require('../services/notify');
const createSlackService = require('../services/slack');
const questionnaireResource = require('./resources/questionnaire-resource');
const createQuestionnaireHelper = require('./questionnaire/questionnaire');
const replaceJsonPointers = require('../services/replace-json-pointer/index');

const defaults = {};
defaults.createQuestionnaireDAL = require('./questionnaire-dal');
Expand Down Expand Up @@ -134,52 +132,6 @@ function createQuestionnaireService({
}
}

async function sendConfirmationNotification(questionnaireId) {
const sharedJsonTranslator = JsonTranslator();
const jsonTranslator = await sharedJsonTranslator;
const questionnaire = await getQuestionnaire(questionnaireId);
try {
const onCompleteTasks = questionnaire.meta.onComplete.tasks;

Object.keys(onCompleteTasks).forEach(taskName => {
const confirmationNotificationConfig = onCompleteTasks[taskName];
const replacedJsonPointersConfig = JSON.parse(
replaceJsonPointers(
JSON.stringify(confirmationNotificationConfig),
questionnaire
)
);
onCompleteTasks[taskName].data = JSON.parse(
jsonTranslator.translate(JSON.stringify(replacedJsonPointersConfig.data), {
vars: replacedJsonPointersConfig.l10n.vars,
translations: replacedJsonPointersConfig.l10n.translations,
data: {
answers: questionnaire.answers
}
})
);
});

if (
onCompleteTasks.sendEmail.data.emailAddress === '' &&
onCompleteTasks.sendSms.data.phoneNumber === ''
) {
return false;
}

const notifyService = createNotifyService({logger});
if (onCompleteTasks.sendEmail.data.emailAddress !== '') {
notifyService.sendEmail(onCompleteTasks.sendEmail.data);
return onCompleteTasks.sendEmail.data;
}
notifyService.sendSms(onCompleteTasks.sendSms.data);
return onCompleteTasks.sendSms.data;
} catch (err) {
logger.error({err}, 'NOTIFICATION SENDING FAILED');
return false;
}
}

async function getSubmissionResponseData(questionnaireId, isPostRequest = false) {
let submissionStatus = await getQuestionnaireSubmissionStatus(questionnaireId);

Expand Down Expand Up @@ -525,6 +477,31 @@ function createQuestionnaireService({
};
}

// TODO: Move this functionality to q-router
async function runOnCompleteActions(questionnaireDefinition) {
const questionnaire = createQuestionnaireHelper({
questionnaireDefinition
});
const permittedActions = questionnaire.getPermittedActions();
const actionResults = permittedActions.map(action => {
if (action.type === 'sendEmail') {
const notifyService = createNotifyService({logger});

return notifyService.sendEmail(action.data);
}

if (action.type === 'sendSms') {
const notifyService = createNotifyService({logger});

return notifyService.sendSms(action.data);
}

return Promise.reject(Error(`Action type "${action.type}" is not supported`));
});

return actionResults;
}

return Object.freeze({
createQuestionnaire,
createAnswers,
Expand All @@ -534,7 +511,7 @@ function createQuestionnaireService({
validateAllAnswers,
getAnswers,
getProgressEntries,
sendConfirmationNotification,
runOnCompleteActions,
updateQuestionnaireSubmissionStatus
});
}
Expand Down
62 changes: 59 additions & 3 deletions questionnaire/questionnaire/questionnaire.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ defaults.mutateObjectValues = require('./utils/mutateObjectValues');
defaults.getValueInterpolator = require('./utils/getValueInterpolator');
defaults.getValueContextualiser = require('./utils/getValueContextualiser');
defaults.deepClone = require('./utils/deepCloneJsonDerivedObject');
defaults.getJsonExpressionEvaluator = require('./utils/getJsonExpressionEvaluator');
defaults.qExpression = require('q-expressions');

function createQuestionnaire({
questionnaireDefinition,
Expand All @@ -17,7 +19,9 @@ function createQuestionnaire({
mutateObjectValues = defaults.mutateObjectValues,
getValueInterpolator = defaults.getValueInterpolator,
getValueContextualiser = defaults.getValueContextualiser,
deepClone = defaults.deepClone
deepClone = defaults.deepClone,
getJsonExpressionEvaluator = defaults.getJsonExpressionEvaluator,
qExpression = defaults.qExpression
}) {
function getProgress() {
return questionnaireDefinition.progress || [];
Expand All @@ -35,6 +39,10 @@ function createQuestionnaire({
return allProgress;
}

function getRoles() {
return questionnaireDefinition?.attributes?.q__roles || {};
}

function getAnswers() {
return questionnaireDefinition.answers;
}
Expand Down Expand Up @@ -208,12 +216,18 @@ function createQuestionnaire({
const valueInterpolator = getValueInterpolator(allQuestionnaireAnswers);

if (sectionDefinition.l10n !== undefined) {
const jsonExpressionEvaluator = getJsonExpressionEvaluator({
...allQuestionnaireAnswers,
attributes: {
q__roles: getRoles()
}
});
const valueContextualier = getValueContextualiser(
sectionDefinition,
allQuestionnaireAnswers
);

orderedValueTransformers.push(valueContextualier);
orderedValueTransformers.push(jsonExpressionEvaluator, valueContextualier);
}

if (sectionDefinitionVars !== undefined && allowSummary === true) {
Expand Down Expand Up @@ -296,14 +310,56 @@ function createQuestionnaire({
return undefined;
}

function getPermittedActions() {
const actions = questionnaireDefinition?.meta?.onComplete?.actions;

if (actions) {
const allQuestionnaireAnswers = {answers: getAnswers()};
const permittedActions = actions.filter(action => {
if ('cond' in action) {
const isPermittedAction = qExpression.evaluate(
action.cond,
allQuestionnaireAnswers
);

return isPermittedAction;
}

return true;
});
const valueInterpolator = getValueInterpolator(allQuestionnaireAnswers);
const jsonExpressionEvaluator = getJsonExpressionEvaluator({
...allQuestionnaireAnswers,
attributes: {
q__roles: getRoles()
}
});
const resolvedActions = permittedActions.map(permittedAction => {
if ('data' in permittedAction) {
mutateObjectValues(permittedAction.data, [
jsonExpressionEvaluator,
valueInterpolator
]);
}

return permittedAction;
});

return resolvedActions;
}

return [];
}

return Object.freeze({
getTaxonomy,
getSection,
getOrderedAnswers,
getDataAttributes,
getNormalisedDetailsForAttribute,
getProgress, // TODO: remove this when declaration is handled correctly
getAnswers // TODO: remove this when declaration is handled correctly
getAnswers, // TODO: remove this when declaration is handled correctly
getPermittedActions
});
}

Expand Down
Loading