not-node 6.3.32 → 6.3.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "not-node",
3
- "version": "6.3.32",
3
+ "version": "6.3.33",
4
4
  "description": "node complimentary part for client side notFramework.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -5,6 +5,8 @@ import entityLayers from "./entityLayers.mjs";
5
5
  import modelValidators from "./modelValidators.mjs";
6
6
  import modelVersioning from "./modelVersioning.mjs";
7
7
  import modelIncrement from "./modelIncrement.mjs";
8
+ import modelDates from "./modelDates.mjs";
9
+ import modelOwnage from "./modelOwnage.mjs";
8
10
 
9
11
  const DEFAULT = {
10
12
  ModelName: "NewModel",
@@ -37,10 +39,14 @@ export default (inquirer, config, layersList) => {
37
39
  result.validators = await modelValidators(inquirer);
38
40
  result.versioning = await modelVersioning(inquirer);
39
41
  result.increment = await modelIncrement(inquirer, result);
42
+ result.ownage = await modelOwnage(inquirer);
43
+ result.dates = await modelDates(inquirer);
40
44
  } else {
41
45
  result.increment = false;
42
46
  result.versioning = false;
43
47
  result.validators = true;
48
+ result.ownage = false;
49
+ result.dates = false;
44
50
  }
45
51
  console.log("Entity data", JSON.stringify(result));
46
52
  return result;
@@ -0,0 +1,14 @@
1
+ export default (inquirer) => {
2
+ return inquirer
3
+ .prompt([
4
+ {
5
+ type: "confirm",
6
+ name: "enabled",
7
+ message: "Model should have createdAt and updatedAt fields",
8
+ default: false,
9
+ },
10
+ ])
11
+ .then((answer) => {
12
+ return answer.enabled;
13
+ });
14
+ };
@@ -0,0 +1,15 @@
1
+ export default (inquirer) => {
2
+ return inquirer
3
+ .prompt([
4
+ {
5
+ type: "confirm",
6
+ name: "enabled",
7
+ message:
8
+ "Model should support ownage by a specific entity (default: User)?",
9
+ default: false,
10
+ },
11
+ ])
12
+ .then((answer) => {
13
+ return answer.enabled;
14
+ });
15
+ };
package/src/form/form.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const validator = require("validator");
2
+ const notPath = require("not-path");
2
3
  const FormFabric = require("./fabric");
3
4
  const { createSchemaFromFields } = require("../fields");
4
5
 
@@ -61,6 +62,19 @@ class Form {
61
62
  #rateLimiterException = FormExceptionTooManyRequests;
62
63
  #rateLimiterClientName = InitRateLimiter.DEFAULT_CLIENT;
63
64
 
65
+ /**
66
+ *
67
+ * @param {Object} options
68
+ * @param {Array<string|Array<string>>} options.FIELDS
69
+ * @param {string} options.FORM_NAME
70
+ * @param {string} options.MODEL_NAME
71
+ * @param {string} options.MODULE_NAME
72
+ * @param {import('../app.js')} options.app
73
+ * @param {Object.<string, Function>} options.EXTRACTORS
74
+ * @param {Object.<string, Function>} options.TRANSFORMERS
75
+ * @param {Object.<string, import('../types.js').notAppFormEnvExtractor>} options.ENV_EXTRACTORS
76
+ * @param {import('../types.js').notAppFormRateLimiterOptions} options.rate
77
+ */
64
78
  constructor({
65
79
  FIELDS,
66
80
  FORM_NAME,
@@ -119,6 +133,7 @@ class Form {
119
133
  **/
120
134
  async run(req) {
121
135
  let data = await this.extract(req);
136
+ data = await this.afterExtract(data, req);
122
137
  await this.#checkRate(data);
123
138
  await this.#_validate(data);
124
139
  return data;
@@ -130,13 +145,10 @@ class Form {
130
145
  * @return {Promise<import('../types').PreparedData>} forma data
131
146
  **/
132
147
  async extract(req) {
133
- return this.afterExtract(
134
- {
135
- ...this.extractRequestEnvs(req),
136
- data: this.extractByInstructionsFromRouteActionFields(req),
137
- },
138
- req
139
- );
148
+ return {
149
+ ...this.extractRequestEnvs(req),
150
+ data: this.extractByInstructionsFromRouteActionFields(req),
151
+ };
140
152
  }
141
153
 
142
154
  /**
@@ -325,76 +337,119 @@ class Form {
325
337
  return FormFabric;
326
338
  }
327
339
 
340
+ /**
341
+ * Object with named extractor functions
342
+ * @param {Object.<string,function>} extractors
343
+ */
328
344
  #addExtractors(extractors = {}) {
329
345
  if (extractors) {
330
346
  this.#EXTRACTORS = { ...this.#EXTRACTORS, ...extractors };
331
347
  }
332
348
  }
333
349
 
350
+ /**
351
+ * Extracts from express Request object data by inststructions object
352
+ * @param {import('../types').notNodeExpressRequest} req
353
+ * @param {import('../types.js').notAppFormProcessingPipe} instructions {fieldName: [extractor, ...transformers]} extractors and transformers should be string (names of functions from libs) or functions
354
+ * @returns {Object}
355
+ */
334
356
  extractByInstructions(req, instructions) {
335
357
  const results = {};
336
358
  for (let fieldName in instructions) {
337
359
  const instruction = instructions[fieldName];
338
360
  if (Array.isArray(instruction)) {
339
- this.#extractByInstructionPipe({
361
+ this.#extractByInstructionPipe(
340
362
  results,
341
- instructions: instruction,
363
+ instruction,
342
364
  fieldName,
343
- req,
344
- });
365
+ req
366
+ );
345
367
  } else {
346
- this.#extractByInstruction({
368
+ this.#extractByInstruction(
347
369
  results,
348
370
  instruction,
349
371
  fieldName,
350
- req,
351
- });
372
+ req
373
+ );
352
374
  }
353
375
  }
354
376
  return results;
355
377
  }
356
378
 
357
- #extractByInstruction({ results, instruction, fieldName, req }) {
379
+ /**
380
+ * Runs one extractor provided as name of extractor from library or as a function.
381
+ * Add extracted data to results object.
382
+ * @param {Object} results
383
+ * @param {import('../types.js').notAppFormPropertyProcessingPipeInstruction} instruction
384
+ * @param {string} fieldName field name, maybe path to sub-object aka data.id
385
+ * @param {import('../types.js').notNodeExpressRequest} req
386
+ * @throws {FormExceptionExtractorForFieldIsUndefined}
387
+ */
388
+ #extractByInstruction(results, instruction, fieldName, req) {
358
389
  if (isFunc(instruction)) {
359
- results[fieldName] = instruction(req, fieldName);
390
+ //using notPath to be able use paths to sub-objects for properties as data.targetId, data.list[0].value etc
391
+ notPath.set(
392
+ notPath.PATH_START_OBJECT + fieldName,
393
+ results,
394
+ // @ts-ignore
395
+ instruction(req, fieldName)
396
+ );
360
397
  } else if (typeof instruction == "string") {
361
398
  const extractor = this.#EXTRACTORS[instruction];
362
399
  if (isFunc(extractor)) {
363
- results[fieldName] = extractor(req, fieldName);
400
+ notPath.set(
401
+ notPath.PATH_START_OBJECT + fieldName,
402
+ results,
403
+ extractor(req, fieldName)
404
+ );
364
405
  } else {
365
406
  throw new FormExceptionExtractorForFieldIsUndefined(fieldName);
366
407
  }
367
408
  }
368
409
  }
369
410
 
370
- #extractByInstructionPipe({ results, instructions, fieldName, req }) {
411
+ /**
412
+ *
413
+ * @param {Object} results resulting object
414
+ * @param {import('../types.js').notAppFormPropertyProcessingPipe} instructions
415
+ * @param {string} fieldName field name, maybe path to sub-object aka data.id
416
+ * @param {import('../types.js').notNodeExpressRequest} req
417
+ */
418
+ #extractByInstructionPipe(results, instructions, fieldName, req) {
371
419
  if (!instructions || instructions.length === 0) {
372
420
  throw new FormExceptionExtractorForFieldIsUndefined(fieldName);
373
421
  }
374
422
  //
375
- this.#extractByInstruction({
376
- results,
377
- instruction: instructions[0],
378
- fieldName,
379
- req,
380
- });
423
+ this.#extractByInstruction(results, instructions[0], fieldName, req);
381
424
  for (let t = 1; t < instructions.length; t++) {
382
425
  const instruction = instructions[t];
383
- this.#transformByInstruction({
384
- results,
385
- instruction,
386
- fieldName,
387
- });
426
+ this.#transformByInstruction(results, instruction, fieldName);
388
427
  }
389
428
  }
390
429
 
391
- #transformByInstruction({ results, instruction, fieldName }) {
430
+ /**
431
+ *
432
+ * @param {Object} results resulting object
433
+ * @param {import('../types.js').notAppFormPropertyProcessingPipeInstruction} instruction
434
+ * @param {string} fieldName field name, maybe path to sub-object aka data.id
435
+ * @throws {FormExceptionTransformerForFieldIsUndefined}
436
+ */
437
+ #transformByInstruction(results, instruction, fieldName) {
392
438
  if (isFunc(instruction)) {
393
- results[fieldName] = instruction(results[fieldName]);
439
+ notPath.set(
440
+ notPath.PATH_START_OBJECT + fieldName,
441
+ results,
442
+ // @ts-ignore
443
+ instruction(results[fieldName])
444
+ );
394
445
  } else if (typeof instruction == "string") {
395
446
  const transformer = this.#TRANSFORMERS[instruction];
396
447
  if (isFunc(transformer)) {
397
- results[fieldName] = transformer(results[fieldName]);
448
+ notPath.set(
449
+ notPath.PATH_START_OBJECT + fieldName,
450
+ results,
451
+ transformer(results[fieldName])
452
+ );
398
453
  } else {
399
454
  throw new FormExceptionTransformerForFieldIsUndefined(
400
455
  fieldName,
@@ -425,6 +480,13 @@ class Form {
425
480
  return [];
426
481
  }
427
482
 
483
+ /**
484
+ *
485
+ * @param {import('../types.js').notNodeExpressRequest} req
486
+ * @param {import('../types.js').notAppFormPropertyProcessingPipe} mainInstruction
487
+ * @param {import('../types.js').notAppFormProcessingPipe} exceptions
488
+ * @returns {import('../types.js').notAppFormProcessingPipe}
489
+ */
428
490
  createInstructionFromRouteActionFields(
429
491
  req,
430
492
  mainInstruction = ["fromBody", "xss"],
@@ -439,26 +501,32 @@ class Form {
439
501
  result[fieldName] = mainInstruction;
440
502
  }
441
503
  });
504
+ // @ts-ignore
442
505
  return result;
443
506
  }
444
507
 
445
508
  /**
446
- *
447
- * @param {import('express').Request} req
448
- * @param {Array<string>} mainInstruction
449
- * @param {object} exceptions
450
- * @returns
509
+ * Creates object {[fieldName]: Array[extractor:string|function, ...transformers:Array<string|function>]}
510
+ * @param {import('../types.js').notNodeExpressRequest} req express request with notRouteData property
511
+ * @param {import('../types.js').notAppFormPropertyProcessingPipe} mainInstruction what is a common pipe to apply to an property [extractor, ...transformers]
512
+ * @param {import('../types.js').notAppFormProcessingPipe} exceptions what shouldn't be treated as common and have own pipes {fieldName:string: [extractor, ...transformers]}
513
+ * @param {import('../types.js').notAppFormProcessingPipe} additional
514
+ * @returns {object}
451
515
  */
452
516
  extractByInstructionsFromRouteActionFields(
453
517
  req,
454
518
  mainInstruction = ["fromBody", "xss"],
455
- exceptions = {}
519
+ exceptions = {},
520
+ additional = {}
456
521
  ) {
457
- const instructions = this.createInstructionFromRouteActionFields(
458
- req,
459
- mainInstruction,
460
- exceptions
461
- );
522
+ const instructions = {
523
+ ...this.createInstructionFromRouteActionFields(
524
+ req,
525
+ mainInstruction,
526
+ exceptions
527
+ ),
528
+ ...additional,
529
+ };
462
530
  return this.extractByInstructions(req, instructions);
463
531
  }
464
532
 
package/src/types.js CHANGED
@@ -95,4 +95,32 @@
95
95
  * @property {string} provider //provider class name
96
96
  */
97
97
 
98
+ /**
99
+ * @typedef {string|function} notAppFormPropertyProcessingPipeInstruction
100
+ */
101
+
102
+ /**
103
+ * @typedef {Array<notAppFormPropertyProcessingPipeInstruction>} notAppFormPropertyProcessingPipe
104
+ */
105
+
106
+ /**
107
+ * @typedef {Object.<string, notAppFormPropertyProcessingPipe>} notAppFormProcessingPipe
108
+ */
109
+
110
+ /**
111
+ * @typedef {object} notAppFormEnvExtractor
112
+ * @property {string} name
113
+ * @property {any} value
114
+ */
115
+
116
+ /**
117
+ * @typedef {object} notAppFormRateLimiterOptions
118
+ * @property {object} [options]
119
+ * @property {string} [options.keyPrefix]
120
+ * @property {number} [options.points]
121
+ * @property {number} [options.duration]
122
+ * @property {object} [exception]
123
+ * @property {function} [idGetter]
124
+ * @property {string} [client]
125
+ */
98
126
  module.exports = {};
@@ -4,32 +4,36 @@ const Form = require("not-node").Form;
4
4
 
5
5
  const FIELDS = [
6
6
  <% if (fields && Array.isArray(fields)) { %>
7
- <% for(let field of fields){ %>
8
- <%- `"${field}",` -%>
9
- <% } %>
10
- <% } %>
11
- ["owner", "not-node//owner"],
12
- ["ownerModel", "not-node//ownerModel"],
13
- ];
14
-
15
- const FORM_NAME = `${MODULE_NAME}:_<%- ModelName %>DataForm`;
16
-
17
-
18
- //const validateTitle = require("./validators/title.js");
19
-
20
- module.exports = class _DataForm extends Form {
21
- constructor({ app }) {
22
- super({ FIELDS, FORM_NAME, app });
23
- }
24
-
25
- extract(data) {
26
- return data;
27
- }
28
-
29
- getFormValidationRules() {
30
- return [
31
- //add validators here
32
- //validateTitle,
33
- ];
34
- }
35
- };
7
+ <% for(let field of fields){ %>
8
+ <%- `"${field}",` -%>
9
+ <% } %>
10
+ <% } %>
11
+ <% if (ownage) { %>
12
+ ["owner", "not-node//owner"],
13
+ ["ownerModel", "not-node//ownerModel"],
14
+ <% } %>
15
+ ];
16
+
17
+ const FORM_NAME = `${MODULE_NAME}:_<%- ModelName %>DataForm`;
18
+
19
+ //const validateTitle = require("./validators/title.js");
20
+ class _<%- ModelName %>_DataForm extends Form {
21
+ constructor({ app }) {
22
+ super({ FIELDS, FORM_NAME, app });
23
+ }
24
+
25
+ //could do something with data, before validation
26
+ async extract(data) {
27
+ return data;
28
+ }
29
+
30
+ //fields validators are loaded automatically, form validators should be returned from here as an array
31
+ getFormValidationRules() {
32
+ return [
33
+ //add validators here
34
+ //validateTitle,
35
+ ];
36
+ }
37
+ }
38
+
39
+ module.exports = _<%- ModelName %>_DataForm;
@@ -2,42 +2,45 @@ const { MODULE_NAME } = require("../const");
2
2
  //DB related validation tools
3
3
  const Form = require("not-node").Form;
4
4
  //form
5
+ const FIELDS = [
6
+ ["data", `${MODULE_NAME}//_<%- ModelName %>_data`],
7
+ ];
5
8
 
6
- const FIELDS = [
7
- ["identity", "not-node//identity"],
8
- ["data", `${MODULE_NAME}//_<%- ModelName %>_data`],
9
- ];
9
+ const FORM_NAME = `${MODULE_NAME}:<%- ModelName %>CreateForm`;
10
10
 
11
- const FORM_NAME = `${MODULE_NAME}:<%- ModelName %>CreateForm`;
11
+ /**
12
+ *
13
+ **/
14
+ class <%- ModelName %>CreateForm extends Form {
15
+ constructor({ app }) {
16
+ super({ FIELDS, FORM_NAME, app });
17
+ }
12
18
 
13
- /**
14
- *
15
- **/
16
- module.exports = class <%- ModelName %>CreateForm extends Form {
17
- constructor({ app }) {
18
- super({ FIELDS, FORM_NAME, app });
19
- }
19
+ /**
20
+ * Extracts data
21
+ * @param {import('not-node/src/types.js').notNodeExpressRequest} req expressjs request object
22
+ * @return {Object} forma data
23
+ **/
24
+ extract(req) {
25
+ const data = this.extractByInstructionsFromRouteActionFields(
26
+ req, //request object
27
+ ["fromBody", "xss"], //extraction common pipe [extractor, ...transformers]
28
+ {} //exceptions {fieldName: [extractor, ...transformers],...}
29
+ );
30
+ //contains targetId, identity and some more. look full list in not-node/src/form/env_extractors/index.js
31
+ const envs = this.extractRequestEnvs(req);
32
+ <% if ( ownage ) { %>
33
+ //admin could change ownage for others hardwired
34
+ if (!identity.admin) {
35
+ data.owner = identity.uid;
36
+ data.ownerModel = 'User';
37
+ }
38
+ <% } %>
39
+ return {
40
+ ...envs,
41
+ data
42
+ };
43
+ }
44
+ }
20
45
 
21
- /**
22
- * Extracts data
23
- * @param {import('not-node/src/types.js').notNodeExpressRequest} req expressjs request object
24
- * @return {Object} forma data
25
- **/
26
- extract(req) {
27
- const instructions = {
28
- <% if (fields && Array.isArray(fields)) { %>
29
- <% for(let field of fields){ %>
30
- <%- field+': "fromBody" ,' -%>
31
- <% } %>
32
- <% } %>
33
- owner: "activeUserId",
34
- ownerModel: "activeUserModelName",
35
- };
36
- const data = this.extractByInstructions(req, instructions);
37
- return {
38
- activeUser: req.user,
39
- ip,
40
- data,
41
- };
42
- }
43
- };
46
+ module.exports = <%- ModelName %>CreateForm;
@@ -5,4 +5,4 @@ module.exports = notNode.Generic.GenericListAndCountForm({
5
5
  MODULE_NAME,
6
6
  MODEL_NAME: "<%- ModelName %>",
7
7
  actionName: "listAll",
8
- });
8
+ });
@@ -5,4 +5,4 @@ module.exports = notNode.Generic.GenericListAndCountForm({
5
5
  MODULE_NAME,
6
6
  MODEL_NAME: "<%- ModelName %>",
7
7
  actionName: "listAndCount",
8
- });
8
+ });
@@ -1,41 +1,33 @@
1
1
  const { MODULE_NAME } = require("../const");
2
2
  const Form = require("not-node").Form;
3
- const getIP = require("not-node").Auth.getIP;
4
3
 
5
4
  const FIELDS = [
6
- ["targetId", { required: true }, "not-node//objectId"],
7
- ["activeUser", "not-node//requiredObject"],
8
- ["data", `${MODULE_NAME}//_<%- ModelName %>_data`], //sub forms validators should start with underscore
9
- ["ip", "not-node//ip"],
10
- ];
11
- const FORM_NAME = `${MODULE_NAME}:<%- ModelName %>UpdateForm`;
5
+ ["targetId", { required: true }, "not-node//objectId"],
6
+ ["identity", "not-node//identity"],
7
+ ["data", `${MODULE_NAME}//_<%- ModelName %>_data`], //sub forms validators should start with underscore
8
+ ];
9
+ const FORM_NAME = `${MODULE_NAME}:<%- ModelName %>UpdateForm`;
12
10
 
13
- class <%- ModelName %>UpdateForm extends Form {
14
- constructor({ app }) {
15
- super({ FIELDS, FORM_NAME, app });
16
- }
11
+ class <%- ModelName %>UpdateForm extends Form {
12
+ constructor({ app }) {
13
+ super({ FIELDS, FORM_NAME, app });
14
+ }
17
15
 
18
- extract(req) {
19
- const instructions = {
20
- <% if (fields && Array.isArray(fields)) { %>
21
- <% for(let field of fields){ %>
22
- <%- field+': "fromBody" ,' -%>
23
- <% } %>
24
- <% } %>
25
- owner: "fromBody",
26
- ownerModel: "fromBody",
27
- };
28
- const data = this.extractByInstructions(req, instructions);
29
- if (!req.user.isRoot() && !req.user.isAdmin()) {
30
- data.owner = req.user._id;
31
- data.ownerModel = "User";
32
- }
33
- return {
34
- targetId: req.params._id.toString(),
35
- activeUser: req.user,
36
- data,
37
- ip: getIP(req),
38
- };
39
- }
40
- }
41
- module.exports = <%- ModelName %>UpdateForm;
16
+ extract(req) {
17
+ const data = this.extractByInstructionsFromRouteActionFields(req, ["fromBody", "xss"], {});
18
+ const envs = this.extractRequestEnvs(req);
19
+ <% if ( ownage ) { %>
20
+ //admin could change ownage for others hardwired
21
+ if (!identity.admin) {
22
+ data.owner && delete data.owner;
23
+ data.ownerModel && delete data.ownerModel;
24
+ }
25
+ <% } %>
26
+ return {
27
+ ...envs, //contains targetId, identity and some more. look list in not-node/src/form/env_extractors/index.js
28
+ data,
29
+ };
30
+ }
31
+ }
32
+
33
+ module.exports = <%- ModelName %>UpdateForm;
@@ -1,32 +1,37 @@
1
1
  const notNode = require("not-node");
2
2
  const { notValidationError } = require("not-error");
3
3
 
4
+ /**
5
+ * async validation function, should throw notValidationError if validation failed
6
+ * @param {object} data form data object
7
+ * @param {object} validationEnvs special object that provides access to injected on project level resources and libraries
8
+ */
4
9
  module.exports = async (
5
10
  /**
6
11
  * Values to validate in form
7
12
  */
8
- {owner, ownerModel, vals, to , validate },
13
+ {owner, ownerModel, vals, to , validate },
9
14
  /**
10
15
  * special container to pass around global validation libs, CONSTs and inject other entities
11
16
  */
12
- validationEnvs
17
+ validationEnvs
13
18
  ) => {
14
- //some preparations
15
- if (
16
- //vals checks
17
- ) {
18
- throw new notValidationError(
19
- "<%- ModuleName %>:validation_error",
20
- {
21
- //vals: ["%- ModuleName %>:vals_should_be_valid"],
22
- },
23
- undefined, //no source error, its custom exception
24
- {
25
- //information
26
- //vals,
27
- //owner,
28
- //ownerModel
29
- }
30
- );
31
- }
32
- };
19
+ //some preparations
20
+ if (
21
+ //vals checks
22
+ ) {
23
+ throw new notValidationError(
24
+ "<%- ModuleName %>:<%- modelName %>_validation_error",
25
+ {
26
+ //vals: ["%- ModuleName %>:vals_should_be_valid"],
27
+ },
28
+ undefined, //no source error, its custom exception
29
+ {
30
+ //information
31
+ //vals,
32
+ //owner,
33
+ //ownerModel
34
+ }
35
+ );
36
+ }
37
+ };