@universis/dining
Version:
Universis api for dining
211 lines (203 loc) • 6.45 kB
JavaScript
import { DataObjectState, DataConfigurationStrategy } from '@themost/data';
import { ApplicationService, TraceUtils } from '@themost/common';
import path from 'path';
class ValidationResult {
/**
* @param {boolean} success
* @param {string=} code
* @param {string=} message
* @param {string=} innerMessage
* @param {any=} data
*/
constructor(success, code, message, innerMessage, data) {
Object.defineProperty(this, 'type', {
value: this.constructor.name,
writable: true,
enumerable: true,
});
this.success =
typeof success !== 'undefined' && success !== null ? success : false;
this.statusCode = this.success ? 200 : 422;
this.code = code;
this.message = message;
this.innerMessage = innerMessage;
this.data = data;
}
}
function instanceOf(any, ctor) {
// validate constructor
if (typeof ctor !== 'function') {
return false;
}
// validate with instanceof
if (any instanceof ctor) {
return true;
}
return !!(any && any.constructor && any.constructor.name === ctor.name);
}
/**
*
* @param {DataEventArgs} event
*/
async function beforeSaveAsync(event) {
// operate only on insert
if (event.state !== DataObjectState.Insert) {
return;
}
const context = event.model.context,
student = context.model('Student').convert(event.target.student),
translateService = context
.getApplication()
.getStrategy(function TranslateService() {});
// validate student request configuration rules
// note: unattended context (registration save) replaces the context.user with the unattendedExecutionAccount
// and so the validate-student-request-configuration after execute listener does not return a validationResult
const studentRequestConfiguration = await context
.model('StudentRequestConfiguration')
.where('additionalType')
.equal('DiningRequestAction')
.select('id')
.silent()
.getTypedItem();
if (studentRequestConfiguration) {
// assign student to request (for validation)
Object.assign(studentRequestConfiguration, {
student,
});
const validationResult = await new Promise((resolve) => {
return studentRequestConfiguration.validate(function (err, result) {
if (err) {
if (instanceOf(err, ValidationResult)) {
return resolve(err);
} else {
return resolve(
new ValidationResult(
false,
'EFAIL',
context.__('An internal server error occurred.'),
err.message
)
);
}
}
return resolve(result);
});
});
// if the student cannot apply for dining due to rules, exit
if (validationResult && validationResult.success === false) {
return;
}
}
// get student dining preference effective status
const diningPreferenceEffectiveStatus =
await student.getDiningPreferenceEffectiveStatus();
// validate effective status existance (edge case)
if (diningPreferenceEffectiveStatus == null) {
return;
}
// switch on the status code
// important note here: The REQUEST_REQUIRED code will only be returned
// if the institute sets the configuration forceRequestOnPositivePreference to true
// otherwise, just an REQUEST_OPTIONAL will be returned
switch (diningPreferenceEffectiveStatus.code) {
case 'NO_ACTION':
return;
case 'REQUEST_OPTIONAL':
return;
case 'PREFERENCE_REQUIRED':
// get dining settings
const diningSettings = context
.getApplication()
.getConfiguration()
.getSourceAt('settings/universis/dining');
const forcePreferenceDeclaration =
typeof (
diningSettings &&
diningSettings.forcePreferenceBeforePeriodRegistration
) === 'boolean'
? diningSettings.forcePreferenceBeforePeriodRegistration
: false;
if (forcePreferenceDeclaration) {
return new ValidationResult(
false,
'EFAIL',
translateService.translate(
'The registration creation failed because the student has not declared their dining request preference.'
)
);
}
return;
case 'REQUEST_REQUIRED':
return new ValidationResult(
false,
'EFAIL',
translateService.translate(
'The registration creation failed because the student has declared a positive dining preference, but has not yet applied for dining.'
)
);
default:
return;
}
}
/**
* @param {DataEventArgs} event
* @param {Function} callback
*/
function beforeSave(event, callback) {
return beforeSaveAsync(event)
.then((validationResult) => {
if (validationResult && !validationResult.success) {
return callback(validationResult);
}
return callback();
})
.catch((err) => {
return callback(err);
});
}
class ValidatePreferenceOnPeriodRegistration extends ApplicationService {
constructor(app) {
super(app);
this.install();
}
install() {
/**
* get data configuration
* @type {DataConfigurationStrategy}
*/
const configuration = this.getApplication()
.getConfiguration()
.getStrategy(DataConfigurationStrategy);
// get StudentRequestAction model definition
const model = configuration.getModelDefinition('StudentPeriodRegistration');
// get this file path relative to application execution path
const listenerType =
'./' +
path.relative(
this.getApplication().getConfiguration().getExecutionPath(),
__filename
);
// ensure model event listeners
model.eventListeners = model.eventListeners || [];
// try to find event listener
const findIndex = model.eventListeners.findIndex((listener) => {
return (
listener.type === listenerType ||
listener.type === listenerType.replace(/\.js$/, '')
);
});
if (findIndex < 0) {
// add event listener
model.eventListeners.push({
type: listenerType,
});
// update StudentRequestAction model definition
configuration.setModelDefinition(model);
// write to log
TraceUtils.info(
`Services (Dining): ${this.constructor.name} service has been successfully installed`
);
}
}
}
export { beforeSave, ValidatePreferenceOnPeriodRegistration };