backend-plus 2.0.0-rc.1 → 2.0.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,11 +6,11 @@ Backend for the anti Pareto rule.
6
6
 
7
7
 
8
8
  ![stable](https://img.shields.io/badge/stability-stable-blue.svg)
9
- [![npm-version](https://img.shields.io/npm/v/backend-star.svg)](https://npmjs.org/package/backend-star)
10
- [![downloads](https://img.shields.io/npm/dm/backend-star.svg)](https://npmjs.org/package/backend-star)
11
- [![build](https://img.shields.io/travis/codenautas/backend-star/master.svg)](https://travis-ci.org/codenautas/backend-star)
12
- [![coverage](https://img.shields.io/coveralls/codenautas/backend-star/master.svg)](https://coveralls.io/r/codenautas/backend-star)
13
- [![dependencies](https://img.shields.io/david/codenautas/backend-star.svg)](https://david-dm.org/codenautas/backend-star)
9
+ [![npm-version](https://img.shields.io/npm/v/backend-plus.svg)](https://npmjs.org/package/backend-plus)
10
+ [![downloads](https://img.shields.io/npm/dm/backend-plus.svg)](https://npmjs.org/package/backend-plus)
11
+ [![build](https://img.shields.io/travis/codenautas/backend-plus/master.svg)](https://travis-ci.org/codenautas/backend-plus)
12
+ [![coverage](https://img.shields.io/coveralls/codenautas/backend-plus/master.svg)](https://coveralls.io/r/codenautas/backend-plus)
13
+ [![dependencies](https://img.shields.io/david/codenautas/backend-plus.svg)](https://david-dm.org/codenautas/backend-plus)
14
14
 
15
15
 
16
16
  language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)
@@ -205,7 +205,7 @@ entry | usage
205
205
  -----------------------------|---------------
206
206
  server |
207
207
  .port | port where is listening
208
- .base-rul | base url added to domain name
208
+ .base-url | base url added to domain name
209
209
  .module-store |
210
210
  install | (see Spanish)
211
211
  .dump | (see Spanish)
@@ -152,7 +152,7 @@ myOwn.wScreens.procAux = {
152
152
  button.disabled=false;
153
153
  divProgressOutside.style.opacity=0.33;
154
154
  toggleProgress.disabled=false;
155
- labelProgress.textContent=resultOk?my.messages.completed:'error';
155
+ labelProgress.textContent=typeof resultOk == "string" ? resultOk : resultOk !== false ? my.messages.completed : 'error';
156
156
  }).catch(function(err){
157
157
  my.log(err);
158
158
  divProgress.textContent=err.message;
@@ -690,11 +690,18 @@ myOwn.tableGrid = function tableGrid(tableName, mainElement, opts){
690
690
  if (myOwn.config.config['grid-row-retain-moved-or-deleted'] && !force) {
691
691
  var depotsToRetain = grid.depots.filter(depot => depot.tick == tick);
692
692
  for (depot of depotsToRetain) {
693
+ depot['$refreshed'] = false
693
694
  if (depot.tr && depot.tr.getAttribute('not-here')) depot.tr.removeAttribute('not-here')
694
695
  }
695
696
  }
696
697
  while (depot = depotsToDelete.pop()) {
697
698
  depot.manager.displayAsDeleted(depot, force ? 'change-ff' : 'unknown');
699
+ if (myOwn.config.config['grid-row-retain-moved-or-deleted']) {
700
+ if(!depot['$refreshed']){
701
+ grid.retrieveRowAndRefresh(depot,{retrieveIgnoringWhere:true})
702
+ depot['$refreshed'] = true
703
+ }
704
+ }
698
705
  }
699
706
  }
700
707
  })
@@ -1615,7 +1622,7 @@ myOwn.dialogDownload = function dialogDownload(grid){
1615
1622
  }else{
1616
1623
  separator='|';
1617
1624
  var trans={
1618
- '|':'\\|',
1625
+ '|':'\\x7C',
1619
1626
  '\\':'\\\\',
1620
1627
  '\r':'\\r',
1621
1628
  '\n':'\\n',
@@ -2385,7 +2392,8 @@ myOwn.TableGrid.prototype.displayGrid = function displayGrid(){
2385
2392
  fixedFields:grid.def.primaryKey.map(function(fieldName, i){
2386
2393
  return {fieldName:fieldName, value:depot.primaryKeyValues[i]};
2387
2394
  }),
2388
- pick:grid.def.pick
2395
+ pick:grid.def.pick,
2396
+ retrieveIgnoringWhere: opts?opts.retrieveIgnoringWhere:false
2389
2397
  }).then(function(result){
2390
2398
  grid.depotRefresh(depot,{updatedRow:result[0], sendedForUpdate:{}}, opts);
2391
2399
  })
@@ -0,0 +1,6 @@
1
+ create or replace function get_app_user() returns text
2
+ stable language sql
3
+ as
4
+ $sql$
5
+ select split_part(current_setting('application_name'),' ',1);
6
+ $sql$;
@@ -35,7 +35,7 @@ export type UploadedFileInfo={
35
35
 
36
36
  }
37
37
  export type CoreFunction = ((context: ProcedureContext, parameters: CoreFunctionParameters) => Promise<any>)
38
- | ((context: ProcedureContext, parameters: CoreFunctionParameters, files?:UploadedFileInfo[]) => Promise<any>);
38
+ | ((context: ProcedureContext, parameters: CoreFunctionParameters, files?:UploadedFileInfo[]) => Promise<any>);
39
39
 
40
40
  export interface ProcedureDef {
41
41
  action: string
@@ -252,6 +252,7 @@ export type Constraint = {constraintType:string, consName?:string} & (
252
252
  {constraintType:'unique', fields:string[], where?:string} |
253
253
  {constraintType:'check', expr?:string}
254
254
  )
255
+ export type OtherTableDefs = Record<string,Partial<TableDefinition & {prefilledField:Record<string,any>}>>
255
256
  export type TableDefinition = EditableDbDefinition & {
256
257
  name:string
257
258
  elementName?:string
@@ -285,15 +286,15 @@ export type TableDefinition = EditableDbDefinition & {
285
286
  viewBody?:string
286
287
  insertIfNotUpdate?:boolean
287
288
  policies?:{
288
- all ?:{using?:string, check?:string}
289
- select?:{using?:string}
290
- insert?:{ check?:string}
291
- update?:{using?:string, check?:string}
292
- delete?:{using?:string}
289
+ all ?:{name?:string, using?:string, check?:string}
290
+ select?:{name?:string, using?:string}
291
+ insert?:{name?:string, check?:string}
292
+ update?:{name?:string, using?:string, check?:string}
293
+ delete?:{name?:string, using?:string}
293
294
  }
294
295
  join?:string
295
296
  constraintsDeferred?:boolean
296
- otherTableDefs?:Record<string,Partial<TableDefinition & {prefilledField:Record<string,any>}>>
297
+ otherTableDefs?:OtherTableDefs
297
298
  }
298
299
  foreignKeys?:ForeignKey[]
299
300
  softForeignKeys?:ForeignKey[]
@@ -322,7 +323,6 @@ export type TableDefinition = EditableDbDefinition & {
322
323
  filterColumns?:{column:string, operator:string, value:any}[],
323
324
  gridAlias?:string /* front-end css my-table = gridAlias */
324
325
  }
325
- export type OtherTableDefs = TableDefinition['sql']['otherTableDefs']
326
326
  export interface DetailTable { table?: string, fields: FieldsForConnectDetailTable, abr: string, label?: string, refreshParent?:boolean, refreshFromParent?:boolean, wScreen?:string, condition?:string }
327
327
  export type TableDefinitionFunction = (context: ContextForDump, opts?:any) => TableDefinition;
328
328
  export type TableItemDef=string|{name:string, path?:string, tableGenerator?:(context:TableContext)=>TableDefinition}
@@ -420,7 +420,8 @@ export class AppBackend{
420
420
  i18n:{
421
421
  messages:Record<LangId,Record<string, string>>
422
422
  }
423
- shootDownBackend():Promise<void>
423
+ shutdownCallbackListAdd(param:{message:string, fun:()=>Promise<void>}):void
424
+ shutdownBackend():Promise<void>
424
425
  setLog(opts:{until:string, results?:boolean}):void
425
426
  getDataDumpTransformations(rawData:string):Promise<{rawData:string, prepareTransformationSql:string[], endTransformationSql:string[]}>
426
427
  }
@@ -54,6 +54,7 @@ var bestGlobals = require('best-globals');
54
54
  var coalesce = bestGlobals.coalesce;
55
55
  var changing = bestGlobals.changing;
56
56
  var datetime = bestGlobals.datetime;
57
+ var splitRawRowIntoRow = bestGlobals.splitRawRowIntoRow;
57
58
  const escapeRegExp = bestGlobals.escapeRegExp;
58
59
  var isLowerIdent = bestGlobals.isLowerIdent;
59
60
 
@@ -96,7 +97,7 @@ class AppBackend{
96
97
  /** @type {{path:string}[]} */
97
98
  this.appStack=[];
98
99
  /** @type {{message:string; fun:()=>void}[]} */
99
- this.shootDownCallbackList=[];
100
+ this.shutdownCallbackList=[];
100
101
  if(!this.rootPath){
101
102
  console.log('ATENCIÓN hay que poner be.rootPath antes de llamar a super()');
102
103
  this.rootPath=Path.resolve(__dirname,'..');
@@ -118,7 +119,7 @@ class AppBackend{
118
119
  this.tableStructures = {};
119
120
  this.configStaticConfig();
120
121
  /** @type {null|()=>Promise<void>} */
121
- this.shootDownBackend = null;
122
+ this.shutdownBackend = null;
122
123
  /** @type {bp.Server} */
123
124
  // @ts-ignore
124
125
  this.server = null;
@@ -405,7 +406,7 @@ function MemoryPerodicallySaved(session){
405
406
  console.log('dumping sessions every',dumpEverySeconds,'seconds');
406
407
  var errorsToShow=4;
407
408
  // TODO: hangs the server in devel mode
408
- setInterval(function(){
409
+ var interval = setInterval(function(){
409
410
  try{
410
411
  fs.writeFile(fileName,json4all.stringify(store.store.dump()));
411
412
  }catch(err){
@@ -416,6 +417,10 @@ function MemoryPerodicallySaved(session){
416
417
  }
417
418
  }
418
419
  },dumpEverySeconds*1000);
420
+ be.shutdownCallbackListAdd({
421
+ message:'session saver',
422
+ fun:()=>clearInterval(interval)
423
+ })
419
424
  });
420
425
  }
421
426
  }
@@ -456,7 +461,7 @@ AppBackend.prototype.setStaticConfig = function setStaticConfig(defConfigYamlStr
456
461
 
457
462
  AppBackend.prototype.configList = function configList(){
458
463
  var list=[this.staticConfig];
459
- if(!this.staticConfig["client-setup"].title && fs.existsSync(this.rootPath+'/def-config.yaml')){
464
+ if(!this.staticConfig["client-setup"]?.title && fs.existsSync(this.rootPath+'/def-config.yaml')){
460
465
  console.log('DEPRECATED!!!!!!')
461
466
  console.error('ERROR el def-config hay que ponerlo dentro de staticConfig');
462
467
  console.log('DEPRECATED!!!!!!')
@@ -466,7 +471,7 @@ AppBackend.prototype.configList = function configList(){
466
471
  list.push(this.rootPath+'/def-config.yaml')
467
472
  };
468
473
  list.push(this.rootPath+'/local-config');
469
- list.push(process.env.BACKEND_PLUS_LOCAL_CONFIG||{});
474
+ list.push(process.env.BACKEND_PLUS_LOCAL_CONFIG?.trim()||{});
470
475
  return list;
471
476
  };
472
477
 
@@ -625,32 +630,36 @@ AppBackend.prototype.start = function start(opts){
625
630
  // @ts-ignore : only for testing */
626
631
  this.getMainApp = function getMainApp(){ return mainApp; };
627
632
  }
628
- this.shootDownBackend = function shootDownBackend(){
633
+ this.shutdownCallbackListAdd = function(messageFun){
634
+ this.shutdownCallbackList.push(messageFun);
635
+ }
636
+ this.shutdownBackend = async function shutdownBackend(){
629
637
  console.log('shooting down:');
630
- return new Promise(function(resolve,reject){
631
- console.log('*','express server');
632
- be.server.close(/** @param {Error} err */function(err){
633
- if(err){
634
- console.log('*', err)
635
- reject(err);
636
- }else{
637
- resolve();
638
- }
639
- });
640
- }).then(async function(){
641
- while(be.shootDownCallbackList.length){
642
- var nextCallback=be.shootDownCallbackList.pop();
643
- if(nextCallback!=null){
644
- console.log('*',nextCallback.message);
645
- await nextCallback.fun();
646
- }
647
- }
648
- }).then(function(){
649
- console.log('pg pool balance',pg.poolBalanceControl());
650
- // @ts-ignore al hacer shootDown ya no hay mainApp.
651
- mainApp = null;
652
- logWhy && logWhy();
653
- });
638
+ var waitFor = [
639
+ new Promise(function(resolve,reject){
640
+ console.log('*','express server');
641
+ be.server.close(/** @param {Error} err */function(err){
642
+ if(err){
643
+ console.log('*', err)
644
+ reject(err);
645
+ }else{
646
+ console.log('*','express server done!')
647
+ resolve();
648
+ }
649
+ });
650
+ }),
651
+ ...(be.shutdownCallbackList.map(x => (async function(){
652
+ console.log('shut:', x.message)
653
+ await x.fun()
654
+ console.log('done:', x.message)
655
+ })()))
656
+ ];
657
+ console.log('*', 'waiting for all')
658
+ await Promise.all(waitFor);
659
+ console.log('*', 'all done')
660
+ mainApp = null;
661
+ console.log('* logWhy',logWhy)
662
+ logWhy && logWhy();
654
663
  };
655
664
  return Promise.resolve().then(function(){
656
665
  var configList=be.configList();
@@ -704,7 +713,7 @@ AppBackend.prototype.start = function start(opts){
704
713
  throw new Error("backend-plus: Motor not recongnized: "+be.config.db.motor);
705
714
  }
706
715
  be.db = pg;
707
- be.dbUserNameExpr="split_part(current_setting('application_name'),' ',1)";
716
+ be.dbUserNameExpr="get_app_user()";
708
717
  be.dbUserRolExpr=`(select ${be.db.quoteIdent(be.config.login.rolFieldName)}
709
718
  from ${be.config.login.schema?be.db.quoteIdent(be.config.login.schema)+'.':''}${be.db.quoteIdent(be.config.login.table)}
710
719
  where ${be.db.quoteIdent(be.config.login.userFieldName)} = ${be.dbUserNameExpr})`
@@ -760,6 +769,7 @@ AppBackend.prototype.start = function start(opts){
760
769
  pg.log.inFileName = 'last-pg-error-local.sql'
761
770
  pg.logLastError.inFileName = 'last-pg-error-local.sql'
762
771
  }
772
+ be.config.db.search_path = be.config.db.search_path ?? [be.config.db.schema, 'public'];
763
773
  be.getDbClient = function getDbClient(req){
764
774
  var paramsDb = be.DoubleDragon?.dbParams?.[req?.user?.[be.config.login.userFieldName]] ?? be.config.db;
765
775
  return pg.connect(paramsDb).then(function(client){
@@ -771,7 +781,7 @@ AppBackend.prototype.start = function start(opts){
771
781
  return client.query(
772
782
  "SET application_name = "+be.db.quoteLiteral(dbAppName)
773
783
  ).execute().then(function(){
774
- var search_path = be.config.db.search_path || [be.config.db.schema, 'public'];
784
+ var search_path = be.config.db.search_path;
775
785
  if(search_path.length>0){
776
786
  return client.query("set SEARCH_PATH TO "+be.db.quoteIdentList(search_path)).execute().then(function(){
777
787
  return client;
@@ -961,12 +971,18 @@ AppBackend.prototype.start = function start(opts){
961
971
  loginPlusOpts.noLoggedUrlPath=be.config.login.unloggedLandPage;
962
972
  }
963
973
  mainApp.loginPlusManager.init(mainApp,changing(be.config.login.plus,loginPlusOpts));
964
- be.shootDownCallbackList.push({
974
+ be.shutdownCallbackListAdd({
965
975
  message:'login-plus manager',
966
976
  fun:function(){
967
977
  mainApp.loginPlusManager.closeManager();
968
978
  }
969
979
  });
980
+ be.shutdownCallbackListAdd({
981
+ message:'pg',
982
+ fun:function(){
983
+ pg.shutdown();
984
+ }
985
+ });
970
986
  mainApp.loginPlusManager.setValidatorStrategy(
971
987
  function(req, username, password, done) {
972
988
  var client;
@@ -1130,7 +1146,7 @@ AppBackend.prototype.start = function start(opts){
1130
1146
  }
1131
1147
  return be.sendMail({
1132
1148
  to: be.config.mailer?.supervise?.to,
1133
- subject: `npm start ${be.config["client-setup"].title || packagejson.name} ok ✔️`,
1149
+ subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} ok ✔️`,
1134
1150
  text:`Inicio del servicio: ${new Date().toJSON()}
1135
1151
 
1136
1152
  Contexto: ${os.userInfo().username} ${process.cwd()}
@@ -1152,7 +1168,7 @@ AppBackend.prototype.start = function start(opts){
1152
1168
  }
1153
1169
  var mailDeAvisoDeFalla = be.sendMail({
1154
1170
  to: be.config.mailer?.supervise?.to,
1155
- subject: `npm start ${be.config["client-setup"].title || packagejson.name} fallido 🛑`,
1171
+ subject: `npm start ${be.config["client-setup"]?.title || packagejson.name} fallido 🛑`,
1156
1172
  text:`Falla en el inicio del servicio: ${new Date().toJSON()}
1157
1173
 
1158
1174
  Contexto: ${os.userInfo().username} ${process.cwd()}
@@ -1209,13 +1225,14 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
1209
1225
  }
1210
1226
  if(be.config.login?.forget){
1211
1227
  try{
1212
- await client.query(`select tokentype, info, due from tokens limit 1`).fetchOneRowIfExists();
1228
+ await client.query(`select tokentype, info, due from his.tokens limit 1`).fetchOneRowIfExists();
1213
1229
  }catch(err){
1214
1230
  var mensaje = `
1215
1231
  --------quizas falten los campos en la tabla tokens:
1216
1232
  alter table tokens add column tokentype text;
1217
1233
  alter table tokens add column info jsonb;
1218
1234
  alter table tokens add column due timestamp;
1235
+ --------quizas falte moverla al esquema his.
1219
1236
  `;
1220
1237
  err.message += mensaje;
1221
1238
  console.log(mensaje)
@@ -1253,6 +1270,21 @@ AppBackend.prototype.checkDatabaseStructure = async function checkDatabaseStruct
1253
1270
  `;
1254
1271
  throw new Error(message);
1255
1272
  }
1273
+ var {rows: sql_routines} = await client.query(`SELECT routine_name, routine_schema, routine_definition
1274
+ FROM information_schema.routines
1275
+ WHERE routine_schema in (${be.config.db.search_path.map(path => be.db.quoteLiteral(path)).join(', ')})
1276
+ `).fetchAll();
1277
+ var sqlRoutines = likeAr.toPlainObject(sql_routines, 'routine_name');
1278
+ var message = ''
1279
+ likeAr(AppBackend.prototype.sql_routines).forEach((routine_name, def) => {
1280
+ if (sqlRoutines[routine_name] && def.dump.includes(sqlRoutines[routine_name].routine_definition)) {
1281
+ message += `
1282
+ ----- hay que crear o actualizar la rutina ${routine_name}:
1283
+ ${dump}
1284
+ `;
1285
+ }
1286
+ })
1287
+ if (message) throw new Error(message);
1256
1288
  };
1257
1289
 
1258
1290
  AppBackend.prototype.postConfig = function postConfig(){
@@ -2181,7 +2213,7 @@ AppBackend.prototype.mainPage = function mainPage(req, offlineMode, opts){
2181
2213
  }
2182
2214
  return html.html(attr,[
2183
2215
  html.head([
2184
- html.title(be.config["client-setup"].title),
2216
+ html.title(be.config["client-setup"]?.title),
2185
2217
  html.meta({charset:"utf-8"}),
2186
2218
  viewportAttrs?html.meta(viewportAttrs):null,
2187
2219
  html.link({href: opts.icons.iconShortcut , rel: "shortcut icon", type: "image/png"}),
@@ -2804,10 +2836,11 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
2804
2836
  lines.push('');
2805
2837
  if(tableDef.sql.policies.enabled){
2806
2838
  policyLines.push(`ALTER TABLE ${cualQuoteTableName} ENABLE ROW LEVEL SECURITY;`);
2807
- ['all', 'select', 'insert', 'update', 'delete'].forEach((command)=>{
2808
- var polcom=tableDef.sql.policies[command];
2809
- if(polcom.using || polcom.check){
2810
- policyLines.push(`CREATE POLICY bp_pol_${command} ON ${cualQuoteTableName} AS PERMISSIVE FOR ${command}`+
2839
+ [null, 'all', 'select', 'insert', 'update', 'delete'].forEach((command)=>{
2840
+ var polcom=tableDef.sql.policies[command] ?? {using: `true`, permissive:true};
2841
+ if(polcom?.using || polcom?.check){
2842
+ policyLines.push(`CREATE POLICY ${be.db.quoteIdent(polcom.name ?? `bp ${command ?? `base`}`)} ON ${cualQuoteTableName} `+
2843
+ `AS ${polcom.permissive ? `PERMISSIVE` : `RESTRICTIVE`} FOR ${command ?? `all`} TO ${be.config.db.user}`+
2811
2844
  (polcom.using? ` USING ( ${polcom.using} )`:'')+
2812
2845
  (polcom.check?` WITH CHECK ( ${polcom.check} )`:'')+';'
2813
2846
  );
@@ -2847,6 +2880,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
2847
2880
  allTableData = allTableContent.slice(startIndex, lastIndex);
2848
2881
  } catch(err) {
2849
2882
  if (err.code != 'ENOENT') throw err;
2883
+ //throw err;
2850
2884
  }
2851
2885
  }
2852
2886
  if (allTableData) {
@@ -2919,7 +2953,7 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
2919
2953
  }else{
2920
2954
  var lines=content.split(/\r?\n/)
2921
2955
  .filter(line => !(/^[-| ]*$/.test(line)) )
2922
- .map(line => line.split(/(?<!(?:^|[^\\])(?:\\\\)*\\)\|/).map(item => item.trimRight().replace(/\\(.)/g,(_,l)=>(l=='t'?'\t':l=='r'?'\r':l=='n'?'\n':l=='s'?' ':l))) )
2956
+ .map(line => splitRawRowIntoRow(line))
2923
2957
  .filter(line => line.length>1 || line.length==1 && line[0].trim() );
2924
2958
  if(lines.length>1){
2925
2959
  if(lines[0][0].startsWith('\ufeff')){
@@ -2964,26 +2998,57 @@ AppBackend.prototype.dumpDbSchemaPartial = async function dumpDbSchemaPartial(pa
2964
2998
  console.log('silence "skipping content" messages in "local-config.yaml".install.dump.skip-content=true');
2965
2999
  }
2966
3000
  }
3001
+ let installFolders = be.config.install.dump.folders ?? ['install']
2967
3002
  let texts = await Promise.all(
2968
3003
  ['prepare.sql','pre-adapt.sql','adapt.sql']
2969
3004
  .concat(be.config.install.dump.scripts['prepare'])
2970
3005
  .concat(be.config.install.dump.scripts['post-adapt'])
2971
- .map(function(fileName){
2972
- return fs.readFile(be.rootPath+'/install/'+fileName, {encoding:'UTF8'}).catch(function(err){
2973
- if(err.code!='ENOENT'){
2974
- throw err;
3006
+ .map(async function(fileName){
3007
+ if (!fileName) return '';
3008
+ var i = 0;
3009
+ var content;
3010
+ do {
3011
+ var folder = installFolders[i];
3012
+ try{
3013
+ content = await fs.readFile(be.rootPath+'/'+folder+'/'+fileName, {encoding:'UTF8'});
3014
+ } catch (err) {
3015
+ if(err.code!='ENOENT') throw err;
2975
3016
  }
2976
- console.log("err:",err);
3017
+ i++;
3018
+ } while (i < installFolders.length && !content);
3019
+ if (!content) {
2977
3020
  return '-- no '+fileName+'\n';
2978
- }).then(function(content){
2979
- return '-- '+fileName+'\n'+content;
2980
- });
3021
+ } else {
3022
+ return '-- '+folder+'/'+fileName+'\n'+content;
3023
+ };
2981
3024
  })
2982
3025
  );
3026
+
3027
+ var common = (await Promise.all(be.appStack.map(async function(stackNode){
3028
+ var common = [];
3029
+ for (var prefix of ['../', '../../']) {
3030
+ try {
3031
+ var dirName = Path.join(stackNode.path,prefix+'install').replace(regexpDistReplacer,'$1$2')
3032
+ var list = await fs.readdir(dirName);
3033
+ } catch (err) {
3034
+ if (err.code != 'ENOENT') throw err;
3035
+ var list = [];
3036
+ }
3037
+ for(var fileName of list){
3038
+ if (fileName.endsWith('-fun.sql')) {
3039
+ common.push(await fs.readFile(Path.join(dirName,fileName), 'utf-8'));
3040
+ }
3041
+ }
3042
+ }
3043
+ return common.join('\n');
3044
+ }))).join('\n');
3045
+
2983
3046
  var prepareList=(be.config.install.dump.scripts['prepare']||[]);
2984
3047
  var mainSql=(
2985
3048
  (complete? linesCreate.join('\n'): '')+
2986
3049
  (complete||opts.forDump? searchPathline.join('\n'): '')+
3050
+ '\n-- common'+
3051
+ common+'\n'+
2987
3052
  (complete? '\n\n--prepare.sql\n'+ texts[0]+'\n\n' :'' )+
2988
3053
  (complete? texts.slice(3,3+prepareList.length).join('\n\n')+'\n\n' : '' )+
2989
3054
  '\n-- functions\n' + functionLines.join('\n')+
@@ -3116,6 +3181,8 @@ AppBackend.prototype.exportacionesGenerico = async function exportacionesGeneric
3116
3181
  if (typeof result[0].title !== "string" || typeof result[0].rows !== "object" && !(result[0].rows instanceof Array) ) {
3117
3182
  throw new Error ("exportacionesGenerico debe recibir {title:string, rows:Record<string, any>[]}")
3118
3183
  }
3184
+ csvFileName = result[0].csvFileName ?? csvFileName;
3185
+ fileName = result[0].fileName ?? fileName;
3119
3186
  await bestGlobals.sleep(100);
3120
3187
  context.informProgress({message:`buscando archivos recién generados`})
3121
3188
  /** @type {{url:string, label:string}[]} */
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
  "use strict";
3
3
 
4
- var {datetime, changing, coalesce} = require('best-globals');
4
+ var {datetime, changing, coalesce, splitRawRowIntoRow} = require('best-globals');
5
5
  var XLSX = require('xlsx');
6
6
  var fs = require('fs/promises')
7
7
  var fsSync = require('fs');
@@ -78,13 +78,14 @@ ProcedureTables = [
78
78
  {name: 'table', encoding:'plain'},
79
79
  {name: 'fixedFields', defaultValue:[]},
80
80
  {name: 'paramfun', defaultValue:[]},
81
- {name: 'pick', defaultValue:'', encoding:'plain'}
81
+ {name: 'pick', defaultValue:'', encoding:'plain'},
82
+ {name: 'retrieveIgnoringWhere', defaultValue:false}
82
83
  ],
83
84
  coreFunction:
84
85
  /**
85
86
  *
86
87
  * @param {*} context
87
- * @param {{table:string, fixedFields:{fieldName:string, value:any, range:string}[], paramfun:string[], pick:string}} parameters
88
+ * @param {{table:string, fixedFields:{fieldName:string, value:any, range:string}[], paramfun:string[], pick:string, retrieveIgnoringWhere:boolean}} parameters * @param {{table:string, fixedFields:{fieldName:string, value:any, range:string}[], paramfun:string[], pick:string}} parameters
88
89
  */
89
90
  async function tableDatum(context, parameters){
90
91
  var be=context.be;
@@ -148,7 +149,7 @@ ProcedureTables = [
148
149
  /** @type {string} */
149
150
  var sql="SELECT "+[...defTable.sql.select].join(', ')+
150
151
  "\n FROM "+defTable.sql.from+
151
- "\n WHERE "+(defTable.sql.where||(defTable.allow.select && !defTable.forInsertOnlyMode?'true':'false'))+fixedClausule.join("")+
152
+ "\n WHERE "+((parameters.retrieveIgnoringWhere?'true':defTable.sql.where)||(defTable.allow.select && !defTable.forInsertOnlyMode?'true':'false'))+fixedClausule.join("")+
152
153
  // " ORDER BY "+defTable.primaryKey.map(be.db.quoteIdent.bind(be.db)).join(',')
153
154
  "\n ORDER BY "+(defTable.sql.orderBy||defTable.primaryKey).map(function(fieldName){ return be.db.quoteIdent(fieldName); }).join(',')
154
155
  if(specialFixedClause.length){
@@ -804,7 +805,8 @@ ProcedureTables = [
804
805
  var rowsForUpsert=lines.slice(1).map(function(line){
805
806
  var othersArray = [];
806
807
  if(separator=='|'){
807
- var row = line.split(/(?<!(?:^|[^\\])(?:\\\\)*\\)\|/).map(item => item.trimRight().replace(/\\(.)/g,(_,l)=>(l=='t'?'\t':l=='r'?'\r':l=='n'?'\n':l=='s'?' ':l)));
808
+ var row = splitRawRowIntoRow(line);
809
+
808
810
  }else{
809
811
  var row=line.split(separator);
810
812
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "backend-plus",
3
3
  "description": "Backend for the anti Pareto rule",
4
- "version": "2.0.0-rc.1",
4
+ "version": "2.0.0-rc.11",
5
5
  "author": "Codenautas <codenautas@googlegroups.com>",
6
6
  "license": "MIT",
7
7
  "repository": "codenautas/backend-plus",
@@ -11,7 +11,8 @@
11
11
  "lib",
12
12
  "for-client",
13
13
  "unlogged",
14
- "src"
14
+ "src",
15
+ "install"
15
16
  ],
16
17
  "contributors": [
17
18
  {
@@ -31,32 +32,32 @@
31
32
  "@upgraded/locate-path": "^6.0.0-alfa.1",
32
33
  "ajax-best-promise": "^0.4.0",
33
34
  "backend-skins": "^0.1.15",
34
- "best-globals": "^1.1.2",
35
+ "best-globals": "^1.1.4",
35
36
  "big.js": "^6.2.1",
36
37
  "body-parser": "^1.20.2",
37
- "cast-error": "^0.1.0",
38
+ "cast-error": "^0.1.1",
38
39
  "castellano": "^0.1.3",
39
40
  "connect-pg-simple": "^9.0.1",
40
41
  "cookie-parser": "^1.4.6",
41
42
  "dialog-promise": "^0.9.15",
42
43
  "discrepances": "^0.2.8",
43
- "express": "^4.18.2",
44
- "express-session": "^1.17.3",
44
+ "express": "^4.19.2",
45
+ "express-session": "^1.18.0",
45
46
  "express-useragent": "^1.0.15",
46
47
  "fs-extra": "^11.2.0",
47
48
  "js-to-html": "^1.3.2",
48
49
  "js-yaml": "^4.1.0",
49
50
  "json4all": "^1.3.0-beta.1",
50
51
  "lazy-some": "^0.1.0",
51
- "like-ar": "^0.3.9",
52
+ "like-ar": "^0.4.0",
52
53
  "login-plus": "^1.7.1",
53
54
  "memorystore": "^1.6.7",
54
55
  "mini-tools": "^1.12.1",
55
56
  "moment": "^2.30.1",
56
57
  "multiparty": "^4.2.3",
57
- "nodemailer": "^6.9.8",
58
+ "nodemailer": "^6.9.13",
58
59
  "numeral": "^2.0.6",
59
- "pg-promise-strict": "^1.3.3",
60
+ "pg-promise-strict": "^1.4.0",
60
61
  "pikaday": "^1.8.2",
61
62
  "pug": "^3.0.2",
62
63
  "read-yaml-promise": "^1.0.2",
@@ -67,8 +68,8 @@
67
68
  "session-file-store": "^1.5.0",
68
69
  "sql-tools": "^0.1.2",
69
70
  "stack-trace": "^0.0.10",
70
- "stylus": "0.62.0",
71
- "type-store": "^0.4.0",
71
+ "stylus": "0.63.0",
72
+ "type-store": "^0.4.1",
72
73
  "typed-controls": "^0.12.0",
73
74
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz"
74
75
  },
@@ -81,7 +82,7 @@
81
82
  "@types/js-yaml": "^4.0.9",
82
83
  "@types/mocha": "^10.0.6",
83
84
  "@types/multiparty": "~0.0.36",
84
- "@types/node": "^20.11.9",
85
+ "@types/node": "^20.12.2",
85
86
  "@types/nodemailer": "^6.4.14",
86
87
  "@types/numeral": "~2.0.5",
87
88
  "@types/session-file-store": "^1.2.5",
@@ -89,20 +90,20 @@
89
90
  "@types/websql": "~0.0.30",
90
91
  "esprima": "^4.0.1",
91
92
  "expect.js": "~0.3.1",
92
- "karma": "6.4.2",
93
+ "karma": "6.4.3",
93
94
  "karma-chrome-launcher": "^3.2.0",
94
95
  "karma-expect": "^1.1.3",
95
- "karma-firefox-launcher": "^2.1.2",
96
+ "karma-firefox-launcher": "^2.1.3",
96
97
  "karma-ie-launcher": "^1.0.0",
97
98
  "karma-mocha": "^2.0.1",
98
99
  "kill-9": "~0.4.3",
99
- "mocha": "^10.2.0",
100
+ "mocha": "^10.4.0",
100
101
  "nyc": "^15.1.0",
101
- "puppeteer": "^21.9.0",
102
+ "puppeteer": "^22.6.1",
102
103
  "sinon": "^17.0.1",
103
104
  "supertest": "^6.3.4",
104
105
  "types.d.ts": "~0.6.21",
105
- "typescript": "^5.3.3",
106
+ "typescript": "^5.4.3",
106
107
  "why-is-node-running": "^2.2.2"
107
108
  },
108
109
  "engines": {