@codetanzania/majifix-priority
Version:
A representation an entity which provides a way to prioritize service and service request(issues) in order of their importance.
597 lines (548 loc) • 15.6 kB
JavaScript
'use strict';
const common = require('@lykmapipo/common');
const env = require('@lykmapipo/env');
const expressRestActions = require('@lykmapipo/express-rest-actions');
const _ = require('lodash');
const mongooseCommon = require('@lykmapipo/mongoose-common');
const mongooseLocaleSchema = require('mongoose-locale-schema');
const actions = require('mongoose-rest-actions');
const exportable = require('@lykmapipo/mongoose-exportable');
const majifixJurisdiction = require('@codetanzania/majifix-jurisdiction');
const majifixCommon = require('@codetanzania/majifix-common');
/* constants */
const OPTION_SELECT = { name: 1, color: 1 };
const OPTION_AUTOPOPULATE = {
select: OPTION_SELECT,
maxDepth: majifixCommon.POPULATION_MAX_DEPTH,
};
const SCHEMA_OPTIONS = { collection: majifixCommon.COLLECTION_NAME_PRIORITY };
const INDEX_UNIQUE = {
jurisdiction: 1,
...mongooseLocaleSchema.localizedIndexesFor('name'),
};
/**
* @module Priority
* @name Priority
* @description A representation an entity which provides a way
* to prioritize service and service request(issues)
* in order of their importance.
*
* @requires https://github.com/CodeTanzania/majifix-jurisdiction
* @author Benson Maruchu <benmaruchu@gmail.com>
* @author lally elias <lallyelias87@gmail.com>
*
* @license MIT
* @since 0.1.0
* @version 1.0.0
* @public
*/
const PrioritySchema = mongooseCommon.createSchema(
{
/**
* @name jurisdiction
* @description jurisdiction under which this priority belongs.
*
* This is applicable where multiple jurisdiction(s) utilize
* same Majifix system(or platform).
*
* If not available a priority is applicable to all jurisdictions.
*
* @type {object}
* @property {object} type - schema(data) type
* @property {string} ref - referenced collection
* @property {boolean} exists - ensure ref exists before save
* @property {object} autopopulate - jurisdiction population options
* @property {object} autopopulate.select - jurisdiction fields to
* select when populating
* @property {boolean} index - ensure database index
*
* @since 0.1.0
* @version 1.0.0
* @instance
*/
jurisdiction: {
type: mongooseCommon.ObjectId,
ref: majifixJurisdiction.Jurisdiction.MODEL_NAME,
exists: {
refresh: true,
select: majifixJurisdiction.Jurisdiction.OPTION_SELECT,
},
autopopulate: majifixJurisdiction.Jurisdiction.OPTION_AUTOPOPULATE,
index: true,
},
/**
* @name name
* @description Human readable name of the priority
* e.g High, Low, Medium.
*
* @type {object}
* @property {object} type - schema(data) type
* @property {boolean} trim - force trimming
* @property {boolean} required - mark required
* @property {boolean} index - ensure database index
* @property {boolean} taggable - allow field use for tagging
* @property {boolean} exportable - allow field to be exported
* @property {boolean} searchable - allow for searching
* @property {object} fake - fake data generator options
*
* @since 0.1.0
* @version 1.0.0
* @instance
*/
name: mongooseLocaleSchema.localize({
type: String,
trim: true,
index: true,
taggable: true,
exportable: true,
searchable: true,
fake: {
generator: 'commerce',
type: 'productName',
unique: true,
},
}),
/**
* @name weight
* @description Weight of the priority to help in ordering
* service request(issue) based on priority.
*
* @type {object}
* @property {object} type - schema(data) type
* @property {boolean} index - ensure database index
* @property {boolean} exportable - allow field to be exported
* @property {boolean} default - default value set when none provided
* @property {object} fake - fake data generator options
*
* @since 0.1.0
* @version 1.0.0
* @instance
*/
weight: {
type: Number,
index: true,
exportable: true,
default: 0,
fake: true,
},
/**
* @name color
* @description A color code used to differentiate a service request
* priority visually.
*
* @type {object}
* @property {object} type - schema(data) type
* @property {boolean} trim - force trimming
* @property {boolean} uppercase - force upper-casing
* @property {boolean} exportable - allow field to be exported
* @property {boolean} default - default value set when none provided
* @property {object} fake - fake data generator options
*
* @since 0.1.0
* @version 1.0.0
* @instance
*/
color: {
type: String,
trim: true,
exportable: true,
uppercase: true,
default: () => common.randomColor(),
fake: true,
},
/**
* @name default
* @description Tells whether a priority is the default.
*
* @type {object}
* @property {object} type - schema(data) type
* @property {boolean} index - ensure database index
* @property {boolean} exportable - allow field to be exported
* @property {boolean} default - default value set when none provided
* @property {object|boolean} fake - fake data generator options
*
* @author lally elias <lallyelias87@gmail.com>
* @since 0.1.0
* @version 0.1.0
* @instance
* @example
* false
*
*/
default: {
type: Boolean,
index: true,
exportable: true,
default: false,
fake: true,
},
},
SCHEMA_OPTIONS,
actions,
exportable
);
/*
*------------------------------------------------------------------------------
* Indexes
*------------------------------------------------------------------------------
*/
/**
* @name index
* @description ensure unique compound index on priority name and jurisdiction
* to force unique priority definition
*
* @author lally elias <lallyelias87@gmail.com>
* @since 0.1.0
* @version 0.1.0
* @private
*/
PrioritySchema.index(INDEX_UNIQUE, { unique: true });
/*
*------------------------------------------------------------------------------
* Hooks
*------------------------------------------------------------------------------
*/
/**
* @name validate
* @description priority schema pre validation hook
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} valid instance or error
* @since 0.1.0
* @version 1.0.0
* @private
*/
PrioritySchema.pre('validate', function preValidate(next) {
return this.preValidate(next);
});
/*
*------------------------------------------------------------------------------
* Instance
*------------------------------------------------------------------------------
*/
/**
* @name preValidate
* @description priority schema pre validation hook logic
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} valid instance or error
* @since 0.1.0
* @version 1.0.0
* @instance
*/
PrioritySchema.methods.preValidate = function preValidate(done) {
// ensure name for all locales
this.name = mongooseLocaleSchema.localizedValuesFor(this.name);
// set default color if not set
if (_.isEmpty(this.color)) {
this.color = common.randomColor();
}
// continue
return done(null, this);
};
/**
* @name beforeDelete
* @function beforeDelete
* @description pre delete priority logics
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} valid instance or error
* @since 0.1.0
* @version 1.0.0
* @instance
*/
PrioritySchema.methods.beforeDelete = function beforeDelete(done) {
// restrict delete if
// collect dependencies model name
const dependencies = [
majifixCommon.MODEL_NAME_SERVICEGROUP,
majifixCommon.MODEL_NAME_SERVICE,
majifixCommon.MODEL_NAME_SERVICEREQUEST,
];
// path to check
const path = majifixCommon.PATH_NAME_PRIORITY;
// do check dependencies
return majifixCommon.checkDependenciesFor(this, { path, dependencies }, done);
};
/*
*------------------------------------------------------------------------------
* Statics
*------------------------------------------------------------------------------
*/
/* static constants */
PrioritySchema.statics.MODEL_NAME = majifixCommon.MODEL_NAME_PRIORITY;
PrioritySchema.statics.OPTION_SELECT = OPTION_SELECT;
PrioritySchema.statics.OPTION_AUTOPOPULATE = OPTION_AUTOPOPULATE;
/**
* @name findDefault
* @function findDefault
* @description find default priority
* @param {Function} done a callback to invoke on success or failure
* @returns {Priority} default priority
*
* @since 0.1.0
* @version 1.0.0
* @static
* @deprecated
*/
PrioritySchema.statics.findDefault = done => {
// refs
const Priority = mongooseCommon.model(majifixCommon.MODEL_NAME_PRIORITY);
// obtain default priority
return Priority.getOneOrDefault({}, done);
};
/**
* @name prepareSeedCriteria
* @function prepareSeedCriteria
* @description define seed data criteria
* @param {object} seed priority to be seeded
* @returns {object} packed criteria for seeding
*
* @author lally elias <lallyelias87@gmail.com>
* @since 1.5.0
* @version 0.1.0
* @static
*/
PrioritySchema.statics.prepareSeedCriteria = seed => {
const names = mongooseLocaleSchema.localizedKeysFor('name');
const copyOfSeed = seed;
copyOfSeed.name = mongooseLocaleSchema.localizedValuesFor(seed.name);
const criteria = common.idOf(copyOfSeed)
? _.pick(copyOfSeed, '_id')
: _.pick(copyOfSeed, 'jurisdiction', ...names);
return criteria;
};
/**
* @name getOneOrDefault
* @function getOneOrDefault
* @description Find existing priority or default based on given criteria
* @param {object} criteria valid query criteria
* @param {Function} done callback to invoke on success or error
* @returns {object|Error} found priority or error
*
* @author lally elias <lallyelias87@gmail.com>
* @since 1.5.0
* @version 0.1.0
* @static
* @example
*
* const criteria = { _id: '...'};
* Priority.getOneOrDefault(criteria, (error, found) => { ... });
*
*/
PrioritySchema.statics.getOneOrDefault = (criteria, done) => {
// normalize criteria
const { _id, ...filters } = common.mergeObjects(criteria);
const allowId = !_.isEmpty(_id);
const allowFilters = !_.isEmpty(filters);
const byDefault = common.mergeObjects({ default: true });
const byId = common.mergeObjects({ _id });
const byFilters = common.mergeObjects(filters);
const or = common.compact([
allowId ? byId : undefined,
allowFilters ? byFilters : undefined,
byDefault,
]);
const filter = { $or: or };
// refs
const Priority = mongooseCommon.model(majifixCommon.MODEL_NAME_PRIORITY);
// query
return Priority.findOne(filter)
.orFail()
.exec(done);
};
/* export priority model */
const Priority = mongooseCommon.model(
majifixCommon.MODEL_NAME_PRIORITY,
PrioritySchema
);
/* constants */
const API_VERSION = env.getString('API_VERSION', '1.0.0');
const PATH_SINGLE = '/priorities/:id';
const PATH_LIST = '/priorities';
const PATH_EXPORT = '/priorities/export';
const PATH_SCHEMA = '/priorities/schema/';
const PATH_JURISDICTION = '/jurisdictions/:jurisdiction/priorities';
/**
* @name PriorityHttpRouter
* @namespace PriorityHttpRouter
*
* @description A representation an entity which provides a way
* to prioritize service and service request(issues)
* in order of their importance.
*
* @author Benson Maruchu <benmaruchu@gmail.com>
* @author lally elias <lallyelias87@gmail.com>
* @license MIT
* @since 0.1.0
* @version 1.0.0
* @public
*/
const router = new expressRestActions.Router({
version: API_VERSION,
});
/**
* @memberof PriorityHttpRouter
* @name GetPriorities
* @description Returns a list of priorities
*/
router.get(
PATH_LIST,
expressRestActions.getFor({
get: (options, done) => Priority.get(options, done),
})
);
/**
* @name GetPrioritySchema
* @memberof PriorityHttpRouter
* @description Returns priority json schema definition
*/
router.get(
PATH_SCHEMA,
expressRestActions.schemaFor({
getSchema: (query, done) => {
const jsonSchema = Priority.jsonSchema();
return done(null, jsonSchema);
},
})
);
/**
* @name ExportPriorities
* @memberof PriorityHttpRouter
* @description Export priorities as csv
*/
router.get(
PATH_EXPORT,
expressRestActions.downloadFor({
download: (options, done) => {
const fileName = `priorities_exports_${Date.now()}.csv`;
const readStream = Priority.exportCsv(options);
return done(null, { fileName, readStream });
},
})
);
/**
* @name PostPriority
* @memberof PriorityHttpRouter
* @description Create new Priority
*/
router.post(
PATH_LIST,
expressRestActions.postFor({
post: (body, done) => Priority.post(body, done),
})
);
/**
* @name GetPriority
* @memberof PriorityHttpRouter
* @description Get existing priority
*/
router.get(
PATH_SINGLE,
expressRestActions.getByIdFor({
getById: (options, done) => Priority.getById(options, done),
})
);
/**
* @name PatchPriority
* @memberof PriorityHttpRouter
* @description Patch existing priority
*/
router.patch(
PATH_SINGLE,
expressRestActions.patchFor({
patch: (options, done) => Priority.patch(options, done),
})
);
/**
* @name PutPriority
* @memberof PriorityHttpRouter
* @description Put existing priority
*/
router.put(
PATH_SINGLE,
expressRestActions.putFor({
put: (options, done) => Priority.put(options, done),
})
);
/**
* @name DeletePriority
* @memberof PriorityHttpRouter
* @description Delete existing priority
*/
router.delete(
PATH_SINGLE,
expressRestActions.deleteFor({
del: (options, done) => Priority.del(options, done),
soft: true,
})
);
/**
* @name GetJurisdictionPriorities
* @memberof PriorityHttpRouter
* @description Returns a list of priorities of specified jurisdiction
*/
router.get(
PATH_JURISDICTION,
expressRestActions.getFor({
get: (options, done) => Priority.get(options, done),
})
);
/**
* @name majifix-priority
* @version 0.1.0
* @description A representation an entity which provides a way
* to prioritize service and service request(issues)
* in order of their importance.
*
* @author Benson Maruchu <benmaruchu@gmail.com>
* @author lally elias <lallyelias87@gmail.com>
* @since 0.1.0
* @version 0.1.0
* @license MIT
* @example
*
* const { Priority, start } = require('@codetanzania/majifix-priority');
* start(error => { ... });
*
*/
/**
* @name info
* @description package information
* @type {object}
*
* @author lally elias <lallyelias87@gmail.com>
* @author rijkerd <richardaggrey7@gmail.com>
* @since 1.0.0
* @version 0.1.0
*/
const info = common.pkg(
`${__dirname}/package.json`,
'name',
'description',
'version',
'license',
'homepage',
'repository',
'bugs',
'sandbox',
'contributors'
);
/**
* @name apiVersion
* @description http router api version
* @type {string}
*
* @author lally elias <lallyelias87@gmail.com>
* @author rijkerd <richardaggrey7@gmail.com>
* @since 0.1.0
* @version 0.1.0
*/
const apiVersion = env.apiVersion();
Object.defineProperty(exports, 'start', {
enumerable: true,
get: function() {
return expressRestActions.start;
},
});
exports.Priority = Priority;
exports.apiVersion = apiVersion;
exports.info = info;
exports.priorityRouter = router;