UNPKG

@universis/dining

Version:

Universis api for dining

292 lines (288 loc) 9.86 kB
import { ApplicationService, DataError, TraceUtils } from '@themost/common'; import { SchemaLoaderStrategy, DataObject, EdmMapping, ModelClassLoaderStrategy, } from '@themost/data'; import path from 'path'; import fs from 'fs'; export class Student extends DataObject { @EdmMapping.func( 'diningPreferenceEffectiveStatus', 'DiningPreferenceEffectiveStatus' ) async getDiningPreferenceEffectiveStatus() { try { const context = this.context; // first of all, get dining settings // and determine if the feature should be available const diningSettings = context .getApplication() .getConfiguration() .getSourceAt('settings/universis/dining'); const diningPreferenceAvailable = typeof (diningSettings && diningSettings.useDiningRequestPreference) === 'boolean' ? diningSettings.useDiningRequestPreference : true; // if not, just return a NO_ACTION code // so this process has no impact on any other service that uses it // (e.g student period registration, study program register action etc) if (!diningPreferenceAvailable) { return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // check if student is active const isActive = await this.isActive(); if (!isActive) { // if not, the student does not have to take // any actions regarding their dining preference declaration return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // get student request configuration for DiningRequestAction // note: an after-execute listener handles request rules validation const studentRequestConfiguration = await context .model('StudentRequestConfiguration') .where('additionalType') .equal('DiningRequestAction') .select('id') .silent() .getItem(); // check if student does not meet the student request configuration rules if ( studentRequestConfiguration && studentRequestConfiguration.validationResult && studentRequestConfiguration.validationResult.success === false ) { // and return a NO_ACTION code return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // get current year const department = await this.property('department') .select('currentYear') .flatten() .getItem(); if (!(department && department.currentYear)) { throw new DataError( 'E_CURRENT_YEAR', `The department's current year cannot be determined.` ); } // try to find if an open dining request event exists for the current year const diningRequestEvent = await context .model('DiningRequestEvent') .where('academicYear') .equal(department.currentYear) .and('eventStatus/alternateName') .equal('EventOpened') .select('id') .getItem(); // if it does not exist if (diningRequestEvent == null) { // student does not have to take any actions regarding their dining preference declaration return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // the actual request has priority, so check it const diningRequestAction = await context .model('DiningRequestAction') .where('student') .equal(this.getId()) .and('diningRequestEvent') .equal(diningRequestEvent.id) .select('id') .getItem(); if (diningRequestAction) { // if a request exists, no extra action has to happen return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // given that an event exists, check the student's preference on that event const diningRequestPreference = await context .model('DiningRequestPreference') .where('student') .equal(this.getId()) .and('diningRequestEvent') .equal(diningRequestEvent.id) .select('id', 'preference') .getItem(); // if preference has not been declared if (diningRequestPreference == null) { // the student has to declare the preference return Object.assign( await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('PREFERENCE_REQUIRED') .getItem(), { diningRequestEvent, } ); } else { // if the student has declared a preference if (diningRequestPreference.preference === false) { // and it is falsy, no action needs to happen return await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } // check if request should be 'forced' const forceRequest = typeof ( diningSettings && diningSettings.forceRequestOnPositivePreference ) === 'boolean' ? diningSettings.forceRequestOnPositivePreference : false; // if the request should be forced if (forceRequest) { // return a REQUEST_REQUIRED status code return Object.assign( await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('REQUEST_REQUIRED') .getItem(), { diningRequestPreference, } ); } // else, just an REQUEST_OPTIONAL status code return Object.assign( await context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('REQUEST_OPTIONAL') .getItem(), { diningRequestPreference, } ); } } catch (err) { // log error TraceUtils.error(err); // and return no action status return await this.context .model('DiningPreferenceEffectiveStatus') .where('code') .equal('NO_ACTION') .getItem(); } } } export class StudentReplacer extends ApplicationService { constructor(app) { super(app); } apply() { try { // get schema loader const schemaLoader = this.getApplication() .getConfiguration() .getStrategy(SchemaLoaderStrategy); // get model definition const model = schemaLoader.getModelDefinition('Student'); const findAttribute = model.fields.find((field) => { return field.name === 'diningCard'; }); if (findAttribute == null) { model.fields.push({ name: 'diningCard', type: 'DiningCard', many: true, multiplicity: 'ZeroOrOne', mapping: { associationType: 'junction', associationAdapter: 'StudentDiningCard', associatedObjectField: 'student', associatedValueField: 'diningCard', cascade: 'delete', parentModel: 'Student', parentField: 'id', childModel: 'DiningCard', childField: 'id', }, }); model.eventListeners = model.eventListeners || []; model.eventListeners.push({ type: path.resolve( __dirname, 'listeners/UpdateStudenDiningCardListener' ), }); schemaLoader.setModelDefinition(model); } // get model class loader const loader = this.getApplication() .getConfiguration() .getStrategy(ModelClassLoaderStrategy); // and student class const StudentClass = loader.resolve(model); // extend existing functions StudentClass.prototype.getDiningPreferenceEffectiveStatus = Student.prototype.getDiningPreferenceEffectiveStatus; // apply also data-localization listener to DiningPreferenceEffectiveStatus model const diningPreferenceEffectiveStatus = schemaLoader.getModelDefinition( 'DiningPreferenceEffectiveStatus' ); if (diningPreferenceEffectiveStatus == null) { return; } // ensure listeners array diningPreferenceEffectiveStatus.eventListeners = diningPreferenceEffectiveStatus.eventListeners || []; // get data-localization listener path const dataLocalizationListener = path.resolve( this.getApplication().getConfiguration().getExecutionPath(), 'listeners/data-localization' ); // ensure correct file path const stat = fs.statSync(`${dataLocalizationListener}.js`); if (!stat.isFile()) { return; } const findListener = diningPreferenceEffectiveStatus.eventListeners.find( (listener) => { return listener.type === dataLocalizationListener; } ); if (findListener) { return; } // add listener, if it does not exist diningPreferenceEffectiveStatus.eventListeners.push({ type: dataLocalizationListener, }); // and set model definition schemaLoader.setModelDefinition(diningPreferenceEffectiveStatus); } catch (err) { TraceUtils.error( 'An error occured while the DiningService tried to extend the Student model (StudentReplacer).' ); TraceUtils.error(err); throw err; } } }