@saltcorn/data 0.6.1-beta.0 → 0.6.1

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.
Files changed (60) hide show
  1. package/base-plugin/actions.js +172 -1
  2. package/base-plugin/fieldviews.js +63 -0
  3. package/base-plugin/fileviews.js +41 -0
  4. package/base-plugin/index.js +35 -0
  5. package/base-plugin/types.js +345 -9
  6. package/base-plugin/viewtemplates/edit.js +107 -0
  7. package/base-plugin/viewtemplates/feed.js +46 -0
  8. package/base-plugin/viewtemplates/filter.js +43 -0
  9. package/base-plugin/viewtemplates/list.js +73 -1
  10. package/base-plugin/viewtemplates/listshowlist.js +54 -3
  11. package/base-plugin/viewtemplates/room.js +100 -0
  12. package/base-plugin/viewtemplates/show.js +86 -0
  13. package/base-plugin/viewtemplates/viewable_fields.js +124 -0
  14. package/contracts.js +58 -0
  15. package/db/connect.js +13 -5
  16. package/db/db.test.js +0 -154
  17. package/db/fixtures.js +13 -1
  18. package/db/index.js +42 -3
  19. package/db/reset_schema.js +11 -0
  20. package/db/state.js +105 -36
  21. package/index.js +13 -0
  22. package/migrate.js +4 -1
  23. package/models/backup.js +78 -0
  24. package/models/config.js +113 -22
  25. package/models/crash.js +44 -0
  26. package/models/discovery.js +13 -11
  27. package/models/email.js +17 -0
  28. package/models/eventlog.js +51 -0
  29. package/models/expression.js +49 -1
  30. package/models/field.js +88 -9
  31. package/models/fieldrepeat.js +33 -0
  32. package/models/file.js +23 -4
  33. package/models/form.js +34 -0
  34. package/models/index.js +42 -0
  35. package/models/layout.js +33 -0
  36. package/models/library.js +44 -0
  37. package/models/pack.js +88 -0
  38. package/models/page.js +13 -3
  39. package/models/plugin.js +9 -2
  40. package/models/random.js +36 -0
  41. package/models/role.js +36 -0
  42. package/models/scheduler.js +28 -0
  43. package/models/table.js +34 -15
  44. package/models/table_constraints.js +44 -0
  45. package/models/tenant.js +46 -9
  46. package/models/trigger.js +24 -11
  47. package/models/user.js +33 -8
  48. package/models/view.js +89 -8
  49. package/models/workflow.js +31 -0
  50. package/package.json +7 -5
  51. package/plugin-helper.js +102 -44
  52. package/plugin-testing.js +4 -0
  53. package/tests/exact_views.test.js +5 -5
  54. package/utils.js +4 -0
  55. package/db/internal.js +0 -229
  56. package/db/multi-tenant.js +0 -24
  57. package/db/pg.js +0 -374
  58. package/db/single-tenant.js +0 -8
  59. package/db/sqlite.js +0 -280
  60. package/db/tenants.js +0 -6
package/models/email.js CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module models/email
4
+ * @subcategory models
5
+ */
1
6
  const nodemailer = require("nodemailer");
2
7
  const { getState } = require("../db/state");
3
8
  const BootstrapEmail = require("bootstrap-email");
@@ -9,6 +14,9 @@ const { v4: uuidv4 } = require("uuid");
9
14
  const db = require("../db");
10
15
  const { mockReqRes } = require("../tests/mocks");
11
16
 
17
+ /**
18
+ * @returns {Transporter}
19
+ */
12
20
  const getMailTransport = () => {
13
21
  const port = getState().getConfig("smtp_port");
14
22
  const secure = getState().getConfig("smtp_secure", port === 465);
@@ -23,6 +31,10 @@ const getMailTransport = () => {
23
31
  });
24
32
  };
25
33
 
34
+ /**
35
+ * @param {object} bsHtml
36
+ * @returns {object}
37
+ */
26
38
  const transformBootstrapEmail = async (bsHtml) => {
27
39
  const filename = await tmp.tmpName();
28
40
  await fs.writeFile(filename, div({ class: "container" }, bsHtml));
@@ -33,6 +45,11 @@ const transformBootstrapEmail = async (bsHtml) => {
33
45
  return email;
34
46
  };
35
47
 
48
+ /**
49
+ * @param {object} user
50
+ * @param {object} [req]
51
+ * @returns {Promise<object>}
52
+ */
36
53
  const send_verification_email = async (user, req) => {
37
54
  const verification_view_name = getState().getConfig("verification_view");
38
55
  if (verification_view_name) {
@@ -1,9 +1,23 @@
1
+ /**
2
+ * EventLog Database Access Layer
3
+ * @category saltcorn-data
4
+ * @module models/eventlog
5
+ * @subcategory models
6
+ */
1
7
  const db = require("../db");
2
8
  const moment = require("moment");
3
9
 
4
10
  const { contract, is } = require("contractis");
5
11
 
12
+ /**
13
+ * EventLog Class
14
+ * @category saltcorn-data
15
+ */
6
16
  class EventLog {
17
+ /**
18
+ * EventLog constructor
19
+ * @param {object} o
20
+ */
7
21
  constructor(o) {
8
22
  this.id = o.id;
9
23
  this.event_type = o.event_type;
@@ -16,14 +30,30 @@ class EventLog {
16
30
  typeof o.payload === "string" ? JSON.parse(o.payload) : o.payload;
17
31
  contract.class(this);
18
32
  }
33
+
34
+ /**
35
+ * @param {object} where
36
+ * @param {object} selopts
37
+ * @returns {Promise<EventLog[]>}
38
+ */
19
39
  static async find(where, selopts) {
20
40
  const us = await db.select("_sc_event_log", where, selopts);
21
41
  return us.map((u) => new EventLog(u));
22
42
  }
43
+
44
+ /**
45
+ * @param {object} where
46
+ * @returns {Promise<EventLog>}
47
+ */
23
48
  static async findOne(where) {
24
49
  const u = await db.selectOne("_sc_event_log", where);
25
50
  return new EventLog(u);
26
51
  }
52
+
53
+ /**
54
+ * @param {string} id
55
+ * @returns {Promise<EventLog>}
56
+ */
27
57
  static async findOneWithUser(id) {
28
58
  const {
29
59
  rows,
@@ -37,12 +67,25 @@ class EventLog {
37
67
  return el;
38
68
  }
39
69
 
70
+ /**
71
+ * @param {object} where
72
+ * @returns {Promise<number>}
73
+ */
40
74
  static async count(where) {
41
75
  return await db.count("_sc_event_log", where || {});
42
76
  }
77
+
78
+ /**
79
+ * @type {string}
80
+ */
43
81
  get reltime() {
44
82
  return moment(this.occur_at).fromNow();
45
83
  }
84
+
85
+ /**
86
+ * @param {object} o
87
+ * @returns {Promise<EventLog>}
88
+ */
46
89
  static async create(o) {
47
90
  const { getState } = require("../db/state");
48
91
 
@@ -64,10 +107,18 @@ class EventLog {
64
107
  return ev;
65
108
  }
66
109
 
110
+ /**
111
+ * @param {string} evType
112
+ * @returns {boolean}
113
+ */
67
114
  static hasTable(evType) {
68
115
  return ["Insert", "Update", "Delete"].includes(evType);
69
116
  }
70
117
 
118
+ /**
119
+ * @param {string} evType
120
+ * @returns {boolean}
121
+ */
71
122
  static hasChannel(evType) {
72
123
  const { getState } = require("../db/state");
73
124
  const t = getState().eventTypes[evType];
@@ -1,7 +1,17 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module models/expression
4
+ * @subcategory models
5
+ */
1
6
  const vm = require("vm");
2
7
  let acorn = require("acorn");
3
8
  const estraverse = require("estraverse");
4
9
  const astring = require("astring");
10
+
11
+ /**
12
+ * @param {string} s
13
+ * @returns {boolean|void}
14
+ */
5
15
  function expressionValidator(s) {
6
16
  if (!s || s.length == 0) return "Missing formula";
7
17
  try {
@@ -11,11 +21,21 @@ function expressionValidator(s) {
11
21
  return e.message;
12
22
  }
13
23
  }
24
+
25
+ /**
26
+ * @param {string} expression
27
+ * @returns {string}
28
+ */
14
29
  function jsexprToSQL(expression) {
15
30
  if (!expression) return expression;
16
31
  return expression.replace(/===/g, "=").replace(/==/g, "=").replace(/"/g, "'");
17
32
  }
18
33
 
34
+ /**
35
+ * @param {string} expression
36
+ * @throws {Error}
37
+ * @returns {object}
38
+ */
19
39
  function jsexprToWhere(expression) {
20
40
  if (!expression) return {};
21
41
  try {
@@ -77,6 +97,11 @@ function jsexprToWhere(expression) {
77
97
  }
78
98
  }
79
99
 
100
+ /**
101
+ * @param {string} expression
102
+ * @param {object[]} statefuns
103
+ * @returns {object}
104
+ */
80
105
  function transform_for_async(expression, statefuns) {
81
106
  var isAsync = false;
82
107
  const ast = acorn.parseExpressionAt(expression, 0, {
@@ -99,6 +124,11 @@ function transform_for_async(expression, statefuns) {
99
124
  return { isAsync, expr_string: astring.generate(ast) };
100
125
  }
101
126
 
127
+ /**
128
+ * @param {string} expression
129
+ * @param {object[]} fields
130
+ * @returns {any}
131
+ */
102
132
  function get_expression_function(expression, fields) {
103
133
  const field_names = fields.map((f) => f.name);
104
134
  const args = field_names.includes("user")
@@ -110,6 +140,13 @@ function get_expression_function(expression, fields) {
110
140
  getState().function_context
111
141
  );
112
142
  }
143
+
144
+ /**
145
+ * @param {string} expression
146
+ * @param {object[]} fields
147
+ * @param {object} [extraContext = {}]
148
+ * @returns {any}
149
+ */
113
150
  function get_async_expression_function(expression, fields, extraContext = {}) {
114
151
  const field_names = fields.map((f) => f.name);
115
152
  const args = field_names.includes("user")
@@ -124,6 +161,11 @@ function get_async_expression_function(expression, fields, extraContext = {}) {
124
161
  });
125
162
  }
126
163
 
164
+ /**
165
+ * @param {object[]} rows
166
+ * @param {object[]} fields
167
+ * @returns {object[]}
168
+ */
127
169
  function apply_calculated_fields(rows, fields) {
128
170
  let hasExprs = false;
129
171
  let transform = (x) => x;
@@ -152,6 +194,12 @@ function apply_calculated_fields(rows, fields) {
152
194
  return rows.map(transform);
153
195
  } else return rows;
154
196
  }
197
+
198
+ /**
199
+ * @param {*} row
200
+ * @param {*} fields
201
+ * @returns {Promise<any>}
202
+ */
155
203
  const apply_calculated_fields_stored = async (row, fields) => {
156
204
  let hasExprs = false;
157
205
  let transform = (x) => x;
@@ -182,7 +230,7 @@ const apply_calculated_fields_stored = async (row, fields) => {
182
230
  };
183
231
  /**
184
232
  * Recalculate calculated columns that are stored in db
185
- * @param table - table object
233
+ * @param {object} table - table object
186
234
  * @returns {Promise<void>}
187
235
  */
188
236
  const recalculate_for_stored = async (table) => {
package/models/field.js CHANGED
@@ -1,12 +1,14 @@
1
1
  /**
2
- *
3
2
  * Field Data Access Layer
3
+ * @category saltcorn-data
4
+ * @module models/field
5
+ * @subcategory models
4
6
  */
5
7
 
6
8
  const db = require("../db");
7
9
  const { contract, is } = require("contractis");
8
10
  const { recalculate_for_stored } = require("./expression");
9
- const { sqlsanitize } = require("../db/internal.js");
11
+ const { sqlsanitize } = require("@saltcorn/db-common/internal.js");
10
12
  const { InvalidAdminAction } = require("../utils");
11
13
  const { mkWhere } = require("../db");
12
14
 
@@ -22,6 +24,7 @@ const readKey = (v, field) => {
22
24
 
23
25
  /**
24
26
  * Field Class
27
+ * @category saltcorn-data
25
28
  */
26
29
  class Field {
27
30
  /**
@@ -99,7 +102,7 @@ class Field {
99
102
 
100
103
  /**
101
104
  * To Json
102
- * @returns {{expression, fieldview, is_unique: (boolean), label: *, table_id: *, type: (string|*), primary_key: (*|boolean), sublabel, required: (boolean), refname: (string|*), reftable_name: (string|*|Table), stored: (*|boolean), name: (*), attributes: any, id, calculated: (*|boolean), reftype: (string|*)}}
105
+ * @type {object}
103
106
  */
104
107
  get toJson() {
105
108
  return {
@@ -126,8 +129,8 @@ class Field {
126
129
 
127
130
  /**
128
131
  * Label 2 Name
129
- * @param label
130
- * @returns {*}
132
+ * @param {string} label
133
+ * @returns {string}
131
134
  */
132
135
  // todo from internalization point of view better to separate label, name. sqlname
133
136
  // because label can contain characters that cannot be used in PG for sql names
@@ -137,7 +140,7 @@ class Field {
137
140
 
138
141
  /**
139
142
  * ???
140
- * @returns {string|*}
143
+ * @returns {string}
141
144
  */
142
145
  get form_name() {
143
146
  if (this.parent_field) return `${this.parent_field}_${this.name}`;
@@ -146,7 +149,8 @@ class Field {
146
149
 
147
150
  /**
148
151
  * Fill fkey options???
149
- * @param force_allow_none
152
+ * @param {boolean} [force_allow_none = false]
153
+ * @param {object} where
150
154
  * @returns {Promise<void>}
151
155
  */
152
156
  async fill_fkey_options(force_allow_none = false, where) {
@@ -177,8 +181,8 @@ class Field {
177
181
 
178
182
  /**
179
183
  * Distinct Values
180
- * @param req
181
- * @returns {Promise<[{label: string, value: string}, {jsvalue: boolean, label, value: string}, {jsvalue: boolean, label, value: string}]|[{label: string, value: string}, ...*]|*[]>}
184
+ * @param {object} [req]
185
+ * @returns {Promise<void>}
182
186
  */
183
187
  async distinct_values(req, where) {
184
188
  const __ = req && req.__ ? req.__ : (s) => s;
@@ -226,6 +230,9 @@ class Field {
226
230
  return [{ label: "", value: "" }, ...dbOpts];
227
231
  }
228
232
 
233
+ /**
234
+ * @type {string}
235
+ */
229
236
  get sql_type() {
230
237
  if (this.is_fkey) {
231
238
  const schema = db.getTenantSchemaPrefix();
@@ -241,12 +248,18 @@ class Field {
241
248
  }
242
249
  }
243
250
 
251
+ /**
252
+ * @type {string}
253
+ */
244
254
  get pretty_type() {
245
255
  if (this.reftable_name === "_sc_files") return "File";
246
256
  if (this.is_fkey) return `Key to ${this.reftable_name}`;
247
257
  else return this.type ? this.type.name : "?";
248
258
  }
249
259
 
260
+ /**
261
+ * @type {string}
262
+ */
250
263
  get sql_bare_type() {
251
264
  if (this.is_fkey) {
252
265
  const { getState } = require("../db/state");
@@ -257,6 +270,9 @@ class Field {
257
270
  }
258
271
  }
259
272
 
273
+ /**
274
+ * @returns {Promise<any>}
275
+ */
260
276
  async generate() {
261
277
  if (this.is_fkey) {
262
278
  const rows = await db.select(
@@ -271,6 +287,10 @@ class Field {
271
287
  }
272
288
  }
273
289
 
290
+ /**
291
+ * @param {object} whole_rec
292
+ * @returns {object}
293
+ */
274
294
  validate(whole_rec) {
275
295
  const type = this.is_fkey ? { name: "Key" } : this.type;
276
296
  const readval = this.is_fkey
@@ -295,26 +315,47 @@ class Field {
295
315
  else return { error: "Not accepted" };
296
316
  }
297
317
 
318
+ /**
319
+ *
320
+ * @param {object} where
321
+ * @param {object} [selectopts]
322
+ * @returns {Field[]}
323
+ */
298
324
  static async find(where, selectopts = { orderBy: "name", nocase: true }) {
299
325
  const db_flds = await db.select("_sc_fields", where, selectopts);
300
326
  return db_flds.map((dbf) => new Field(dbf));
301
327
  }
302
328
 
329
+ /**
330
+ * @param {object} where
331
+ * @returns {Promise<Field>}
332
+ */
303
333
  static async findOne(where) {
304
334
  const db_fld = await db.selectOne("_sc_fields", where);
305
335
  return new Field(db_fld);
306
336
  }
307
337
 
338
+ /**
339
+ * @returns {Promise<void>}
340
+ */
308
341
  async add_unique_constraint() {
309
342
  await this.fill_table();
310
343
  await db.add_unique_constraint(this.table.name, [this.name]);
311
344
  }
312
345
 
346
+ /**
347
+ * @returns {Promise<void>}
348
+ */
313
349
  async remove_unique_constraint() {
314
350
  await this.fill_table();
315
351
  await db.drop_unique_constraint(this.table.name, [this.name]);
316
352
  }
317
353
 
354
+ /**
355
+ *
356
+ * @param {boolean} not_null
357
+ * @returns {Promise<void>}
358
+ */
318
359
  async toggle_not_null(not_null) {
319
360
  const schema = db.getTenantSchemaPrefix();
320
361
  await this.fill_table();
@@ -328,6 +369,10 @@ class Field {
328
369
  await require("../db/state").getState().refresh_tables();
329
370
  }
330
371
 
372
+ /**
373
+ * @param {object} new_field
374
+ * @returns {Promise<void>}
375
+ */
331
376
  async alter_sql_type(new_field) {
332
377
  let new_sql_type = new_field.sql_type;
333
378
  let def = "";
@@ -365,6 +410,9 @@ class Field {
365
410
  await require("../db/state").getState().refresh_tables();
366
411
  }
367
412
 
413
+ /**
414
+ * @returns {Promise<void>}
415
+ */
368
416
  async fill_table() {
369
417
  if (!this.table) {
370
418
  const Table = require("./table");
@@ -372,6 +420,10 @@ class Field {
372
420
  }
373
421
  }
374
422
 
423
+ /**
424
+ * @param {object} v
425
+ * @returns {Promise<void>}
426
+ */
375
427
  async update(v) {
376
428
  if (
377
429
  typeof v.is_unique !== "undefined" &&
@@ -405,6 +457,10 @@ class Field {
405
457
  });
406
458
  await require("../db/state").getState().refresh_tables();
407
459
  }
460
+
461
+ /**
462
+ * @type {string}
463
+ */
408
464
  get listKey() {
409
465
  return this.type.listAs
410
466
  ? (r) => this.type.listAs(r[this.name])
@@ -412,6 +468,10 @@ class Field {
412
468
  ? (r) => this.type.showAs(r[this.name])
413
469
  : this.name;
414
470
  }
471
+
472
+ /**
473
+ * @type {object}
474
+ */
415
475
  get presets() {
416
476
  if (this.type && this.type.presets) return this.type.presets;
417
477
 
@@ -420,6 +480,11 @@ class Field {
420
480
 
421
481
  return null;
422
482
  }
483
+
484
+ /**
485
+ * @throws {InvalidAdminAction}
486
+ * @returns {Promise<void>}
487
+ */
423
488
  async delete() {
424
489
  const Table = require("./table");
425
490
  const table = await Table.findOne({ id: this.table_id });
@@ -457,6 +522,10 @@ class Field {
457
522
  await require("../db/state").getState().refresh_tables();
458
523
  }
459
524
 
525
+ /**
526
+ * @param {object} table
527
+ * @returns {Promise<void>}
528
+ */
460
529
  async enable_fkey_constraint(table) {
461
530
  if (this.is_fkey && !db.isSQLite) {
462
531
  const schema = db.getTenantSchemaPrefix();
@@ -472,6 +541,11 @@ class Field {
472
541
  }
473
542
  }
474
543
 
544
+ /**
545
+ * @param {object} fld
546
+ * @param {boolean} [bare = false]
547
+ * @returns {Promise<Field>}
548
+ */
475
549
  static async create(fld, bare = false) {
476
550
  const f = new Field(fld);
477
551
  const schema = db.getTenantSchemaPrefix();
@@ -568,6 +642,11 @@ class Field {
568
642
  return f;
569
643
  }
570
644
 
645
+ /**
646
+ * @param {function|object[]} [typeattribs]
647
+ * @param {number} [table_id]
648
+ * @returns {*}
649
+ */
571
650
  static getTypeAttributes(typeattribs, table_id) {
572
651
  const Table = require("./table");
573
652
 
@@ -1,7 +1,21 @@
1
+ /**
2
+ * FieldRepeat Database Access Layer
3
+ * @category saltcorn-data
4
+ * @module models/fieldrepeat
5
+ * @subcategory models
6
+ */
1
7
  const { contract, is } = require("contractis");
2
8
  const Field = require("./field");
3
9
 
10
+ /**
11
+ * FieldRepeat Class
12
+ * @category saltcorn-data
13
+ */
4
14
  class FieldRepeat {
15
+ /**
16
+ * FieldRepeat constructor
17
+ * @param {object} o
18
+ */
5
19
  constructor(o) {
6
20
  this.label = o.label || o.name;
7
21
  this.name = o.name;
@@ -11,6 +25,10 @@ class FieldRepeat {
11
25
  this.isRepeat = true;
12
26
  contract.class(this);
13
27
  }
28
+
29
+ /**
30
+ * @returns {Promise<void>}
31
+ */
14
32
  async generate() {
15
33
  const nrepeats = Math.round(Math.random() * 5);
16
34
  var r = {};
@@ -22,9 +40,20 @@ class FieldRepeat {
22
40
  }
23
41
  }
24
42
  }
43
+
44
+ /**
45
+ * @param {*} whole_rec
46
+ * @returns {object}
47
+ */
25
48
  validate(whole_rec) {
26
49
  return this.validate_from_ix(whole_rec, 0);
27
50
  }
51
+
52
+ /**
53
+ * @param {*} whole_rec
54
+ * @param {*} ix
55
+ * @returns {object}
56
+ */
28
57
  validate_from_ix(whole_rec, ix) {
29
58
  var has_any = false;
30
59
  var res = {};
@@ -41,6 +70,10 @@ class FieldRepeat {
41
70
  return { success: [res, ...rest.success] };
42
71
  } else return { success: [] };
43
72
  }
73
+
74
+ /**
75
+ * @type {string}
76
+ */
44
77
  get form_name() {
45
78
  return this.name;
46
79
  }
package/models/file.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
- *
3
- * @type {{changeConnection?: ((function(*): Promise<void>)|(function(*=): Promise<void>)), select?: ((function(*=, *=, *=): Promise<*>)|(function(*=, *=, *=): Promise<*>)), runWithTenant: ((function(*=, *=): (*))|(function(*, *): *)), set_sql_logging?: (function(*=): void), insert?: ((function(*=, *=, *=): Promise<undefined|*>)|(function(*=, *=, *=): Promise<*>)), update?: ((function(*=, *=, *=): Promise<void>)|(function(*=, *=, *, *=): Promise<void>)), sql_log?: (function(*=, *=): void), deleteWhere?: ((function(*=, *=): Promise<void>)|(function(*=, *=): Promise<*>)), isSQLite: boolean, selectMaybeOne?: ((function(*=, *=): Promise<null|*>)|(function(*=, *=): Promise<null|*>)), close?: ((function(): Promise<void>)|(function(): Promise<void>)), drop_unique_constraint?: ((function(*=, *): Promise<void>)|(function(*=, *): Promise<void>)), enable_multi_tenant: (function()), getVersion?: ((function(): Promise<*>)|(function(*=): Promise<*>)), add_unique_constraint?: ((function(*=, *): Promise<void>)|(function(*=, *): Promise<void>)), getTenantSchema: ((function(): *)|(function(): *)), is_it_multi_tenant: ((function(): boolean)|(function(): boolean)), sqliteDatabase?: *, drop_reset_schema?: ((function(): Promise<void>)|(function(*): Promise<void>)), query?: ((function(*=, *=): Promise<unknown>)|(function(*=, *=): *)), count?: ((function(*=, *=): Promise<number>)|(function(*=, *=): Promise<number>)), pool?: *, connectObj: {sc_version: string, connectionString: *, git_commit: *, version_tag: (*|string)}|{sqlite_path}|boolean, sqlsanitize: *|(function(...[*]=): *), getClient?: (function(): Promise<*>), reset_sequence?: (function(*=): Promise<void>), copyFrom?: (function(*=, *=, *, *): Promise<void>), mkWhere: function(*=): {values: *, where: string|string}, selectOne?: ((function(*=, *=): Promise<*|undefined>)|(function(*=, *=): Promise<*>)), getTenantSchemaPrefix: function(): string|string}|{sqlsanitize?: *|(function(...[*]=): *), connectObj?: {sc_version: string, connectionString: *, git_commit: *, version_tag: (*|string)}|{sqlite_path}|boolean, isSQLite?: boolean, mkWhere?: function(*=): {values: *, where: string|string}, getTenantSchemaPrefix?: function(): string|string}}
2
+ * File Database Access Layer
3
+ * @category saltcorn-data
4
+ * @module models/file
5
+ * @subcategory models
4
6
  */
5
7
 
6
8
  const db = require("../db");
@@ -19,8 +21,13 @@ const fs = require("fs").promises;
19
21
  * 3. List of files stored in _sc_files table in Saltcorn database.
20
22
  * 4. Each tenant has own file list and file storage.
21
23
  * 5. This class provides file descriptor and basic functions to manipulate with files.
24
+ * @category saltcorn-data
22
25
  */
23
26
  class File {
27
+ /**
28
+ * Constructor
29
+ * @param o {object}
30
+ */
24
31
  constructor(o) {
25
32
  this.filename = o.filename;
26
33
  this.location = o.location;
@@ -43,7 +50,14 @@ class File {
43
50
  * @param selectopts
44
51
  * @returns {Promise<*>}
45
52
  */
46
- static async find(where, selectopts) {
53
+ static async find(where, selectopts = {}) {
54
+ if (selectopts.cached) {
55
+ const { getState } = require("../db/state");
56
+ const files = Object.values(getState().files).sort((a, b) =>
57
+ a.filename > b.filename ? 1 : -1
58
+ );
59
+ return files.map((t) => new File(t));
60
+ }
47
61
  const db_flds = await db.select("_sc_files", where, selectopts);
48
62
  return db_flds.map((dbf) => new File(dbf));
49
63
  }
@@ -106,7 +120,7 @@ class File {
106
120
  * @param file
107
121
  * @param user_id
108
122
  * @param min_role_read
109
- * @returns {Promise<File|[]>}
123
+ * @returns {Promise<File>}
110
124
  */
111
125
  static async from_req_files(file, user_id, min_role_read = 1) {
112
126
  if (Array.isArray(file)) {
@@ -150,6 +164,11 @@ class File {
150
164
  return { error: e.message };
151
165
  }
152
166
  }
167
+
168
+ /**
169
+ * MIME type of the file
170
+ * @type {string}
171
+ */
153
172
  get mimetype() {
154
173
  return `${this.mime_super}/${this.mime_sub}`;
155
174
  }