const { DB, DEFAULT_QUERY_OPTIONS, ANCHORING_DOMAIN } = require('../constants');
const Manager = require("../../pdm-dsu-toolkit/managers/Manager");
const {toPage, paginate} = require("../../pdm-dsu-toolkit/managers/Page");
/**
* ShipmentLine Manager Class
*
* Manager Classes in this context should do the bridge between the controllers
* and the services exposing only the necessary api to the controllers while encapsulating <strong>all</strong> business logic.
*
* All Manager Classes should be singletons.
*
* This complete separation of concerts is very beneficial for 2 reasons:
* <ul>
* <li>Allows for testing since there's no browser dependent code (i think) since the DSUStorage can be 'mocked'</li>
* <li>Allows for different controllers access different business logic when necessary (while benefiting from the singleton behaviour)</li>
* </ul>
*
* @param {ParticipantManager} participantManager
* @param {function(err, Manager)} [callback] optional callback for when the assurance that the table has already been indexed is required.
* @class ShipmentLineManager
* @extends Manager
* @memberOf Managers
*/
class ShipmentLineManager extends Manager {
constructor(participantManager, callback) {
super(participantManager, DB.shipmentLines, ['gtin', 'createdOn', 'batch', 'status', 'requesterId', 'senderId'], (err, manager) => {
if (err)
return callback ? callback(err) : console.log(err);
manager.registerMessageListener((message, cb) => {
manager.processMessageRecord(message, (err) => {
manager.refreshController();
cb(err);
});
});
if (callback)
callback(undefined, manager);
});
this.shipmentLineService = new (require('../services/ShipmentLineService'))(ANCHORING_DOMAIN);
}
/**
* Util function that loads a ShipmentLineDSU and reads its information
* @param {string|KeySSI} keySSI
* @param {function(err, ShipmentLine, Archive)} callback
* @protected
* @override
*/
_getDSUInfo(keySSI, callback){
return this.shipmentLineService.get(keySSI, callback);
}
/**
* generates the db's key for the ShipmentLine
* @param {string|number} requesterId
* @param {string|number} senderId
* @param {string|number} gtin
* @param {string|number} createdOn
* @return {string}
* @protected
*/
_genCompostKey(requesterId, senderId, gtin, createdOn){
return `${requesterId}-${senderId}-${gtin}-${createdOn}`;
}
/**
* Must wrap the DB entry in an object like:
* <pre>
* {
* index1: ...
* index2: ...
* value: item
* }
* </pre>
* so the DB can be queried by each of the indexes and still allow for lazy loading
* @param {string} key
* @param {ShipmentLine} item
* @param {string|object} record
* @return {object} the indexed object to be stored in the db
* @protected
* @override
*/
_indexItem(key, item, record) {
return {
gtin: item.gtin,
createdOn: item.createdOn,
batch: item.batch,
status: item.status.status,
requesterId: item.requesterId,
senderId: item.senderId,
value: record
}
};
/**
* reads ssi for that OrderLine in the db. loads is and reads the info at '/info' and the status at '/status/info
* @param {string} key
* @param {boolean} [readDSU] defaults to true. decides if the manager loads and reads from the dsu or not
* @param {function(err, object|KeySSI, Archive)} callback returns the Product if readDSU and the dsu, the keySSI otherwise
*/
getOne(key, readDSU, callback) {
if (!callback){
callback = readDSU;
readDSU = true;
}
let self = this;
self.getRecord(key, (err, itemSSI) => {
if (err)
return self._err(`Could not load record with key ${key} on table ${self._getTableName()}`, err, callback);
if (!readDSU)
return callback(undefined, itemSSI);
self.shipmentLineService.get(itemSSI.value || itemSSI, callback);
});
}
/**
* Lists all received orders.
* @param {boolean} [readDSU] defaults to true. decides if the manager loads and reads from the dsu's {@link INFO_PATH} or not
* @param {object} [options] query options. defaults to {@link DEFAULT_QUERY_OPTIONS}
* @param {function(err, Order[])} callback
*/
getAll(readDSU, options, callback) {
const defaultOptions = () => Object.assign({}, DEFAULT_QUERY_OPTIONS, {
query: ['date > 0'],
sort: 'dsc'
});
if (!callback) {
if (!options) {
callback = readDSU;
options = defaultOptions();
readDSU = true;
}
if (typeof readDSU === 'boolean') {
callback = options;
options = defaultOptions();
}
if (typeof readDSU === 'object') {
callback = options;
options = readDSU;
readDSU = true;
}
}
options = options || defaultOptions();
let self = this;
self.query(options.query, options.sort, options.limit, (err, records) => {
if (err)
return self._err(`Could not perform query`, err, callback);
if (!readDSU)
return callback(undefined, records.map(r => r.pk));
self._iterator(records.map(r => r.value), self.shipmentLineService.get, (err, result) => {
if (err)
return self._err(`Could not parse ${self._getTableName()}s ${JSON.stringify(records)}`, err, callback);
console.log(`Parsed ${result.length} ${self._getTableName()}s`);
callback(undefined, result);
});
});
}
_processMessageRecord(message, callback) {
let self = this;
if (!message || typeof message !== "string")
return callback(`Message ${message} does not have non-empty string with keySSI. Skipping record.`);
let shipmentLines;
try {
shipmentLines = JSON.parse(message);
} catch (e) {
shipmentLines = [message];
}
const lines = [];
const shipmentLineIterator = function(linesCopy, callback){
const lineSSI = linesCopy.shift();
if (!lineSSI)
return callback(undefined, lines);
self._getDSUInfo(lineSSI, (err, shipmentLine, shipmentLineDsu) => {
if (err) {
console.log(`Could not read DSU from message keySSI in record ${message}. Skipping record.`);
return callback();
}
const compostKey = self._genCompostKey(shipmentLine.requesterId, shipmentLine.senderId, shipmentLine.gtin, shipmentLine.createdOn);
const cb = function(err){
if (err)
return self._err(`Could not insert/update record for ShipmentLine ${compostKey}`, err, callback);
shipmentLineIterator(linesCopy, callback);
}
self.getRecord(compostKey, (err, record) => {
if (err){
console.log(`Received ShipmentLine`, shipmentLine);
return self.insertRecord(compostKey, self._indexItem(undefined, shipmentLine, lineSSI), cb);
}
console.log(`Updating ShipmentLine`, shipmentLine);
self.updateRecord(compostKey, self._indexItem(undefined, shipmentLine, lineSSI), cb);
});
});
}
const dbAction = function(shipmentLines, lines, callback){
const cbErr = function(err, ...results){
if (err)
return self.cancelBatch(err2 => {
callback(err);
});
callback(undefined, ...results);
}
try {
self.beginBatch();
} catch (e){
return self.batchSchedule(() => dbAction(shipmentLines, lines, callback));
//return callback(e);
}
shipmentLineIterator(shipmentLines.slice(), (err, newLines) => {
if (err)
return cbErr(`Could not register all shipmentlines`);
self.commitBatch((err) => {
if(err)
return cbErr(err);
console.log(`ShipmentLines successfully registered: ${JSON.stringify(newLines)}`);
callback(undefined, lines);
});
});
}
dbAction(shipmentLines, lines, callback);
};
/**
* updates a shipmentLine
*
* @param {string} [key] key is optional so child classes can override them
* @param {ShipmentLine} shipmentLine
* @param {function(err, Shipment?, Archive?)} callback
*/
update(key, shipmentLine, callback){
if (!callback){
callback = shipmentLine;
shipmentLine = key;
key = this._genCompostKey(shipmentLine.requesterId, shipmentLine.senderId, shipmentLine.gtin, shipmentLine.createdOn);
}
let self = this;
self.getRecord(key, (err, record) => {
if (err)
return self._err(`Unable to retrieve record with key ${key} from table ${self._getTableName()}`, err, callback);
self.updateRecord(key, self._indexItem(key, shipmentLine, record.value), (err) => {
if (err)
return self._err(`Unable to update record with key ${key} from table ${self._getTableName()}`, err, callback);
callback(undefined, shipmentLine, record.value);
});
});
}
}
/**
* @param {ParticipantManager} participantManager
* @param {function(err, Manager)} [callback] optional callback for when the assurance that the table has already been indexed is required.
* @returns {ShipmentLineManager}
* @memberOf Managers
*/
const getShipmentLineManager = function (participantManager, callback) {
let manager;
try {
manager = participantManager.getManager(ShipmentLineManager);
if (callback)
return callback(undefined, manager);
} catch (e){
manager = new ShipmentLineManager(participantManager, callback);
}
return manager;
}
module.exports = getShipmentLineManager;
Source