@universis/candidates
Version:
Universis api server plugin for study program candidates, internship selection etc
175 lines (170 loc) • 7.23 kB
JavaScript
// eslint-disable-next-line no-unused-vars
import { DataObjectState, DataConfigurationStrategy, DataContext } from '@themost/data';
import { ApplicationService, TraceUtils, DataError, DataNotFoundError } from '@themost/common';
import path from 'path';
import { getMailer } from '@themost/mailer';
/**
*
* @param {DataEventArgs} event
*/
async function afterSaveAsync(event) {
const context = event.model.context;
let shouldContinue = false;
// get candidate student user
const user = await context.model(event.model.name)
.where('id').equal(event.target.id)
.select('user').silent().value();
if (event.state === DataObjectState.Insert) {
shouldContinue = (user != null);
} else if (event.state === DataObjectState.Update) {
const previous = event.previous;
if (previous == null) {
throw new DataError('E_STATE', 'Invalid configuration. The previous state of an object cannot be determined.', null, 'CandidateStudent');
}
shouldContinue = (previous.user == null) && (user != null);
}
if (shouldContinue === false) {
// exit
return;
}
// validate sms service
const service = context.getApplication().getService(function SmsService() {});
if (service == null) {
throw new Error('Invalid application configuration. Messaging service is missing or is inaccessible state.');
}
// send sms message
// first of all get register web application
const application = await context.model('WebApplication')
.where('alternateName').equal('register')
.and('applicationSuite').equal('universis')
.silent().getItem();
if (application == null) {
throw new DataNotFoundError('A required object for account activation notification is missing. User cannot be notified for this action.', null, 'WebApplication');
}
// get candidate
const candidate = await context.model('CandidateStudent')
.where('user').equal(user).expand('person').silent().getItem();
const candidateUser = await context.model('CandidateUser')
.where('id').equal(user).silent().getItem();
// assign user
Object.assign(candidate, {
user: candidateUser
});
if (candidate.user == null) {
throw new DataNotFoundError('Candidate user cannot be found. The operation cannot be completed.', null, 'CandidateUser', 'activationCode');
}
if (candidate.user.activationCode == null) {
throw new DataError('E_USER', 'User state is invalid. The operation cannot be completed.', null, 'CandidateUser', 'activationCode');
}
const mailer = getMailer(context);
let mailTemplate = await context.model('MailConfiguration')
.where('target').equal('NotifyCandidateUserAction')
.and('state').equal(DataObjectState.Update)
.silent().getItem();
if (mailTemplate == null) {
// throw error
throw new DataNotFoundError('Sms template for account activation notification is missing. User cannot be notified for this action.', null, 'MailConfiguration');
}
const result = await new Promise((resolve, reject) => {
mailer.template(mailTemplate.template)
.test(true)
.send({
model: {
candidate,
application
}
}, (err, body) => {
if (err) {
TraceUtils.error('NotifyCandidateUserAction', 'An error occurred while trying to send an sms notification for account activation.');
return reject(err);
}
// send message
return resolve(body);
});
});
// create message
await context.model('SMS').silent().save({
sender: context.user,
recipient: user,
body: result.html
});
}
/**
* @param {DataEventArgs} event
* @param {Function} callback
*/
function afterSave(event, callback) {
return afterSaveAsync(event).then(() => {
return callback();
}).catch((err) => {
return callback(err);
});
}
class SendSmsAfterCreateCandidateUser extends ApplicationService {
/**
* @param {*=} app
*/
constructor(app) {
// call super constructor
super(app);
if (app != null) {
// if application is defined, install service
this.install('CandidateUser', __filename);
}
}
install() {
/**
* get data configuration
* @type {DataConfigurationStrategy}
*/
const configuration = this.getApplication().getConfiguration().getStrategy(DataConfigurationStrategy);
// get StudentRequestAction model definition
const model = configuration.getModelDefinition('CandidateStudent');
// 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: ${this.constructor.name} service has been successfully installed`);
}
}
/**
* @param {DataContext} context
* @param {{id: number, user: *}} candidate
*/
async send(context, candidate) {
const CandidateStudents = context.model('CandidateStudent');
// get candidate and validate
const target = await CandidateStudents.where('id').equal(candidate).getTypedItem();
if (target == null) {
TraceUtils.error('SendSmsAfterCreateCandidateUser', 'An attempt of sending sms notification to a candidate failed because the specified candidate was not found.');
throw new DataNotFoundError('Candidate cannot be found or is inaccessible', null, 'CandidateStudent');
}
if (target.user == null) {
TraceUtils.error('SendSmsAfterCreateCandidateUser', 'An attempt of sending sms notification to a candidate failed because the specified candidate does not have an associated user or user was not found.');
throw new DataNotFoundError('User cannot be found or is inaccessible', null, 'CandidateUser');
}
await afterSaveAsync({
model: CandidateStudents, // set event model
state: DataObjectState.Insert, // set state to insert to force sending message
target: target // set candidate as target object
});
return true;
}
}
export {
afterSave,
SendSmsAfterCreateCandidateUser
}