const express = require('express');
const isAnonymousAllowed = require("../data/_AnonymousAllowed");
const jsDataSet = require("./../../client/components/metadata/jsDataSet");
const _ = require("lodash");
const asyncHandler = require('express-async-handler'); //https://zellwk.com/blog/async-await-express/
const metaModel = require("./../../client/components/metadata/MetaModel");
const attachUtils = require("./../../client/components/metadata/_attachmentutils");
const {DataSet} = require("./../../client/components/metadata/jsDataSet");
const jsBusinessLogic =require("../../src/jsBusinessLogic");
const {BusinessMessage} = require("../../src/jsBusinessLogic");
async function saveDataSet(req,res,next) {
let ctx = req.app.locals.context;
let ds = new DataSet();
ds.deSerialize(JSON.parse(req.body.ds), true);
let tableName = req.body.tableName;
let editType = req.body.editType;
try {
let messagesJson = req.body.messages; //id, description, audit, severity, table, bool canIgnore
let messages = null;
if (messagesJson) {
messages = JSON.parse(messagesJson);
}
let meta = ctx.getMeta(tableName);
if (!meta) {
res.status(500).json({error: "Not valid entity:" + tableName});
return;
}
meta.editType = editType;
if (!isAnonymousAllowed(req, tableName, editType, ds)) {
res.status(400).send("Anonymous access not allowed");
}
let postData = ctx.createPostData.call(ctx);
let prevResult = postData.createBusinessLogicResult();
if (messages) { //messages is an array of id, description, audit, severity, table, bool canIgnore
for (const m of messages) {
prevResult.addMessage(deserializeMessage(m));
}
}
let isValid = true;
await forEachAsync(Object.keys(ds.tables), async (t) => {
const table = ds.tables[t];
if (t !== tableName && !metaModel.isSubEntity(table, ds.tables[tableName])) return true;
let tName = table.tableForReading();
let currMeta = ctx.getMeta(tName);
if (!currMeta) {
res.status(500).json({error: "Not valid entity:" + tName});
return;
}
currMeta.setRequest(req);
currMeta.editType = editType;
currMeta.ds = ds;
await forEachAsync(table.rows, async (row) => {
let DR = row.getRow();
if (DR.state === jsDataSet.dataRowState.unchanged) return true;
let resValid;
try {
resValid = await currMeta.isValid(DR);
if (resValid === null) return true;
isValid = false;
// at run time for serialization id property is evalued as
// this.description = description;
// this.audit = audit;
// this.severity = severity; // "Errore" || "Avvertimento" || "Disabilitata"
// this.table = table;
// this.canIgnore = canIgnore; // true/false
//
prevResult.push(new jsBusinessLogic.BusinessMessage({
r: DR,
post: false, // considero come pre
shortMsg: resValid.errMsg, //errore breve, per le business rule
//Serialized as description
longMsg: `Tabella: ${tName} campo: ${resValid.errField} err: {resValid.errMsg}`,
canIgnore: false,
idDetail: "validazione",
//serialized as audit
idRule: "validazione",
environment: this.environment
}));
} catch (e) {
isValid = false;
prevResult.push(new jsBusinessLogic.BusinessMessage({
r: DR,
post: false, // considero come pre
shortMsg: resValid.errMsg, //errore breve, per le business rule
//Serialized as description
longMsg: `Bisogna rivedere il metodo isValid della tabella: ${tName} err: {e}`,
canIgnore: false,
idDetail: "validazione",
//serialized as audit
idRule: "Errore Metadato",
environment: this.environment
}));
}
return true;
});
return true;
});
if (!isValid) {
let dsJson = JSON.stringify(ds.serialize(true));
res.json({
dataset: dsJson,
messages: prevResult.map(m => serializeMessage(m)),
success: false,
canIgnore: false
});
return;
}
let outDs = await ctx.getDataInvoke.createEmptyDataSet(tableName, editType);
if (!outDs) {
res.send(400, "DataSet non esistente");
return;
}
_.forEach(outDs.tables, (table) => {
let dtInput = ds.tables[table.name];
if (dtInput) {
table.merge(dtInput);
// copio le prop di autoincremento
table.autoIncrementColumns = _.cloneDeep(dtInput.autoIncrementColumns);
}
});
await postData.init(outDs, ctx);
// 7. valuto se ci sono tabelle con allegati, cioè colonna idattach per convenzione
// se ci sono costruisco un dsattach nuovo dove inserisco la logica dei contatori
// che serve per gestire l'algoritmo di persistenza degli allegati, in base all'operazione
// creo ds attach in cui inserirò la nuova riga sulla tabella attach. Questa operazione
// va fatta in questo punto perchè il save del dell'allegato è fatto in un altra chiamata, e devo tenere
// persistenti e sincronizzate le 2 sorgenti, filesystem e db. quindi gestisco con i contatori
// su una tabella del db
let dsattach = await attachUtils.getDsAttachWithCounterUpdated(ctx, outDs);
if (dsattach) {
await postData.init(dsattach, ctx);
}
let dataRowAttachModified = await attachUtils.manageAttachWindowsCompliant(ctx, outDs);
manageRegistration(outDs);
let postResult = await postData.doPost({previousRules: prevResult});
let success = true;
// sarà true se tutti i messaggi sono ignorabili, false se almeno 1 messaggio non è ignorabile
// se ci sono messaggi , significa che la transazione non è stata eseguita, e devo amndare messaggi opportuni al client.
let canIgnore = true;
if (postResult.checks.length > 0) {
success = false;
canIgnore = postResult.canIgnore;
attachUtils.sanitizeDsForAttachUnsuccess(dataRowAttachModified);
}
else {
await attachUtils.removeAttachmentAfterSuccess(dataRowAttachModified, ctx);
await attachUtils.sanitizeDsForAttach(outDs, ctx);
}
let dsSerialized = outDs.serialize(true);
res.json({
dataset: dsSerialized,
messages: postResult.checks.map(m => serializeMessage(m)),
success: success,
canIgnore: postResult.canIgnore
});
}
catch(ex){
res.status(500).send("saveDatSet(tableName="+tableName+",editType="+editType + ' ' + ex.stack );
}
}
function manageRegistration(ds) {
// prevista ma non fa nulla per ora
}
async function forEachAsync(arr, fn) {
for (let t of arr) { await fn(t); }
}
/**
* @typedef BusinessMessageData
* @property id:string
* @property description:string
* @property audit:string
* @property severity:string
* @property table:string
* @property canIgnore:bool
*/
/**
*
* @param {BusinessMessage} msg
* @return {BusinessMessageData}
*/
function serializeMessage(msg){
let table= msg.__table;
if (!table) {
}
let pre_post = msg.post?"post":"pre";
let id="dberror";
//new messages
if (msg.idDetail && msg.rowChange){
let operation = "D";
let /*RowChange*/ DR = msg.rowChange.r;
table= DR.tableName;
if (DR.state ===jsDataSet.dataRowState.added) operation="I";
if (DR.state ===jsDataSet.dataRowState.modified) operation="U";
return {
id:pre_post+"/"+table+"/"+operation+"/"+msg.idDetail,
description: msg.getMessage(),
audit:msg.idRule,
severity: msg.canIgnore? "W":"E",
table: table,
canIgnore: msg.canIgnore
};
}
//old messages
return {
id:msg.__id, //
description: msg.getMessage(),
audit:msg.idRule,
severity: msg.canIgnore? "W":"E",
table: msg.__table,
canIgnore: msg.canIgnore
};
}
/**
* Deserialize a message
* @param {BusinessMessageData} msg
* @return {BusinessMessage}
*/
function deserializeMessage(msg){
// id = pre_post + "/" + pm.TableName + "/" + pm.Operation.Substring(0, 1) + "/" + pm.EnforcementNumber
let operation="X";
let post = false;
let bm;
if (msg.id !== "dberror") {
let id_parts = msg.id.split("/");
if (id_parts[0] === "post") {
post = true;
}
operation = msg[1];
bm = new BusinessMessage({
post: post,
shortMsg: msg.audit,
canIgnore: msg.canIgnore,
idDetail: id_parts[3],
idRule: msg.audit,
longMsg: msg.description
});
}
else {
bm = new BusinessMessage({
post: false,
shortMsg: msg.audit,
canIgnore: msg.canIgnore,
idDetail: null,
idRule: msg.audit
});
}
bm.__table = msg.table;
bm.__id = msg.id;
return bm;
}
let router = express.Router();
router.post('/saveDataSet', asyncHandler(saveDataSet));
module.exports= router;