@universis/candidates
Version:
Universis api server plugin for study program candidates, internship selection etc
216 lines (211 loc) • 7.07 kB
JavaScript
import { DataConfigurationStrategy, DataObjectState } from '@themost/data';
import { ApplicationService, DataError, TraceUtils } from '@themost/common';
import { inc, valid, coerce } from 'semver';
import path from 'path';
import { insertListener } from './utils/insertListener';
/**
*
* @param {DataEventArgs} event
*/
async function afterSaveAsync(event) {
// operate only on update (active to completed status)
if (
!(
event.state === DataObjectState.Update &&
Object.prototype.hasOwnProperty.call(event.target, 'actionStatus')
)
) {
return;
}
const context = event.model.context;
// before everything, validate the required service/models that this listener handles
// first, the dining service itself
const diningService = context
.getApplication()
.getStrategy(function DiningService() {});
if (diningService == null) {
TraceUtils.info(
'[StudentCarePreferenceService]: DiningService is not enabled and thus a DiningRequestPreference cannot be created.'
);
return;
}
// then, validate DiningPreference
// note: at a later stage, the getDiningPreferenceEffective student status method is validated
// which covers the existance of all the other related models
const diningPreferenceModel = context.model('DiningRequestPreference');
if (diningPreferenceModel == null) {
TraceUtils.info(
'[StudentCarePreferenceService]: DiningRequestPreference model is absent in the current application context and thus a DiningRequestPreference cannot be created/validated.'
);
return;
}
// get previous action status
const previousActionStatus = event.previous && event.previous.actionStatus;
if (previousActionStatus == null) {
throw new DataError(
'E_PREVIOUS_STATUS',
'The previous action status of the object cannot be empty at this context.',
null,
'StudyProgramRegisterAction'
);
}
// and target action status
const targetActionStatus = await context
.model('ActionStatusType')
.find(event.target.actionStatus)
.select('alternateName')
.value();
// if this is not an accept action, exit
if (
!(
previousActionStatus.alternateName === 'ActiveActionStatus' &&
targetActionStatus === 'CompletedActionStatus'
)
) {
return;
}
// get the dining preference value from the register action
const enrollmentDiningPreference = await context
.model('StudyProgramRegisterAction')
.where('id')
.equal(event.target.id)
.select('intendsToApplyForDining')
.silent()
.value();
// important: proceed only if enrollmentDiningPreference is of type boolean
// which means that it has actually been declared by the student
// this field, of course, does not have a default value and may be null
if (typeof enrollmentDiningPreference !== 'boolean') {
return;
}
// ensure that a student has been created for the candidate
const student = context
.model('Student')
.convert(
await context
.model('CandidateStudent')
.find(event.previous.candidate)
.select('student as id')
.flatten()
.getItem()
);
if (student == null) {
return;
}
// validate getDiningPreferenceEffectiveStatus method
if (typeof student.getDiningPreferenceEffectiveStatus !== 'function') {
TraceUtils.info(
'[StudentCarePreferenceService]: The DiningPreference creation is not supported by the current DiningService version.'
);
return;
}
// get diningRequestEvent event from effective status method
const { diningRequestEvent } =
await student.getDiningPreferenceEffectiveStatus();
// and validate it
if (diningRequestEvent == null) {
return;
}
// create a dining preference
const diningRequestPreference = {
student: student.id,
diningRequestEvent,
preference: enrollmentDiningPreference,
};
// and save silently
await diningPreferenceModel.silent().save(diningRequestPreference);
}
/**
* @param {DataEventArgs} event
* @param {Function} callback
*/
function afterSave(event, callback) {
return afterSaveAsync(event)
.then(() => {
return callback();
})
.catch((err) => {
return callback(err);
});
}
class StudentCarePreferenceService extends ApplicationService {
constructor(app) {
super(app);
this.install();
}
install() {
try {
/**
* get data configuration
* @type {DataConfigurationStrategy}
*/
const configuration = this.getApplication()
.getConfiguration()
.getStrategy(DataConfigurationStrategy);
// get StudyProgramRegisterAction model definition
const model = configuration.getModelDefinition(
'StudyProgramRegisterAction'
);
if (model == null) {
throw new Error(
'The StudyProgramRegisterAction model cannot be found in the current application context, or it has not been loaded yet.'
);
}
// ensure model fields
model.fields = model.fields || [];
let updateModelDefinition = false;
// apply extra attributes/fields
[
{
name: 'intendsToApplyForDining',
title: 'Dining preference',
description:
'Indicates whether the student intends to receive dining services from the university facilities.',
type: 'Boolean',
},
{
name: 'intendsToApplyForHousing',
title: 'Housing preference',
description:
'Indicates whether the student intends to receive housing services from the university facilities.',
type: 'Boolean',
},
].forEach((attribute) => {
// try to find attribute
const attributeExists = model.fields.find(
(field) => field.name === attribute.name
);
if (attributeExists) {
return;
}
// if it does not exist, add it to the fields collection
model.fields.push(attribute);
updateModelDefinition = true;
});
// prepare listener
const listener = {
// get this file path relative to application execution path
type: './' + path.relative(this.getApplication().getConfiguration().getExecutionPath(), __filename),
insertBefore: 'send-mail-after-status-change'
}
// insert listener
updateModelDefinition = insertListener(listener, model) || updateModelDefinition;
if (!updateModelDefinition) {
return;
}
// update model version for migrations
model.version = inc(valid(coerce(model.version || '1.0.0')), 'minor');
configuration.setModelDefinition(model);
// write to log
TraceUtils.info(
`Services: ${this.constructor.name} service has been successfully installed.`
);
} catch (err) {
TraceUtils.error(
`An error occured while trying to install ${this.constructor.name} service.`
);
TraceUtils.error(err);
}
}
}
export { afterSave, StudentCarePreferenceService };