@nocobase/database 0.9.1-alpha.2 → 0.9.2-alpha.2

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 (168) hide show
  1. package/lib/collection-group-manager.d.ts +13 -0
  2. package/lib/collection-group-manager.js +91 -0
  3. package/lib/collection-importer.js +0 -24
  4. package/lib/collection.d.ts +24 -3
  5. package/lib/collection.js +176 -236
  6. package/lib/database-utils/index.js +3 -15
  7. package/lib/database.d.ts +3 -0
  8. package/lib/database.js +160 -298
  9. package/lib/decorators/must-have-filter-decorator.js +0 -7
  10. package/lib/decorators/transaction-decorator.js +5 -18
  11. package/lib/errors/identifier-error.js +0 -3
  12. package/lib/features/ReferencesMap.js +1 -14
  13. package/lib/features/referential-integrity-check.js +7 -21
  14. package/lib/field-repository/array-field-repository.js +5 -45
  15. package/lib/fields/array-field.js +0 -13
  16. package/lib/fields/belongs-to-field.js +24 -50
  17. package/lib/fields/belongs-to-many-field.js +23 -47
  18. package/lib/fields/boolean-field.js +0 -7
  19. package/lib/fields/context-field.js +2 -23
  20. package/lib/fields/date-field.d.ts +4 -0
  21. package/lib/fields/date-field.js +15 -7
  22. package/lib/fields/field.js +32 -85
  23. package/lib/fields/has-many-field.js +16 -49
  24. package/lib/fields/has-one-field.js +18 -52
  25. package/lib/fields/index.js +0 -44
  26. package/lib/fields/json-field.js +0 -12
  27. package/lib/fields/number-field.js +0 -23
  28. package/lib/fields/password-field.js +8 -35
  29. package/lib/fields/radio-field.js +0 -18
  30. package/lib/fields/relation-field.js +4 -16
  31. package/lib/fields/set-field.js +0 -8
  32. package/lib/fields/sort-field.js +84 -73
  33. package/lib/fields/string-field.js +0 -7
  34. package/lib/fields/text-field.js +0 -7
  35. package/lib/fields/time-field.js +0 -7
  36. package/lib/fields/uid-field.js +4 -22
  37. package/lib/fields/uuid-field.js +3 -12
  38. package/lib/fields/virtual-field.js +0 -7
  39. package/lib/filter-match.js +7 -22
  40. package/lib/filter-parser.js +37 -101
  41. package/lib/index.d.ts +3 -0
  42. package/lib/index.js +36 -42
  43. package/lib/inherited-collection.js +15 -62
  44. package/lib/inherited-map.js +7 -48
  45. package/lib/listeners/adjacency-list.d.ts +3 -0
  46. package/lib/listeners/adjacency-list.js +91 -0
  47. package/lib/listeners/index.d.ts +2 -0
  48. package/lib/listeners/index.js +12 -0
  49. package/lib/magic-attribute-model.js +58 -114
  50. package/lib/migration.js +7 -28
  51. package/lib/mock-database.d.ts +4 -4
  52. package/lib/mock-database.js +15 -18
  53. package/lib/model-hook.js +4 -35
  54. package/lib/model.js +12 -54
  55. package/lib/operators/array.js +2 -32
  56. package/lib/operators/association.js +0 -6
  57. package/lib/operators/boolean.js +0 -6
  58. package/lib/operators/child-collection.d.ts +2 -0
  59. package/lib/operators/child-collection.js +32 -0
  60. package/lib/operators/date.js +123 -60
  61. package/lib/operators/empty.js +3 -32
  62. package/lib/operators/eq.d.ts +2 -0
  63. package/lib/operators/eq.js +26 -0
  64. package/lib/operators/index.js +4 -7
  65. package/lib/operators/ne.js +5 -5
  66. package/lib/operators/notIn.js +0 -5
  67. package/lib/operators/string.js +0 -11
  68. package/lib/operators/utils.js +0 -6
  69. package/lib/options-parser.d.ts +1 -1
  70. package/lib/options-parser.js +47 -107
  71. package/lib/playground.js +0 -4
  72. package/lib/query-interface/mysql-query-interface.d.ts +11 -0
  73. package/lib/query-interface/mysql-query-interface.js +59 -10
  74. package/lib/query-interface/postgres-query-interface.d.ts +8 -0
  75. package/lib/query-interface/postgres-query-interface.js +70 -12
  76. package/lib/query-interface/query-interface-builder.js +0 -5
  77. package/lib/query-interface/query-interface.d.ts +12 -0
  78. package/lib/query-interface/query-interface.js +33 -3
  79. package/lib/query-interface/sqlite-query-interface.d.ts +11 -0
  80. package/lib/query-interface/sqlite-query-interface.js +61 -10
  81. package/lib/relation-repository/belongs-to-many-repository.js +21 -78
  82. package/lib/relation-repository/belongs-to-repository.js +0 -3
  83. package/lib/relation-repository/hasmany-repository.js +8 -44
  84. package/lib/relation-repository/hasone-repository.js +0 -3
  85. package/lib/relation-repository/multiple-relation-repository.js +14 -68
  86. package/lib/relation-repository/relation-repository.js +5 -42
  87. package/lib/relation-repository/single-relation-repository.js +5 -43
  88. package/lib/repository.d.ts +1 -0
  89. package/lib/repository.js +36 -182
  90. package/lib/sql-parser/index.js +10527 -0
  91. package/lib/sql-parser/sql.pegjs +1297 -0
  92. package/lib/sync-runner.js +19 -54
  93. package/lib/update-associations.js +58 -157
  94. package/lib/update-guard.js +10 -49
  95. package/lib/utils.js +6 -54
  96. package/lib/value-parsers/array-value-parser.js +3 -21
  97. package/lib/value-parsers/base-value-parser.js +0 -13
  98. package/lib/value-parsers/boolean-value-parser.js +4 -10
  99. package/lib/value-parsers/date-value-parser.js +0 -23
  100. package/lib/value-parsers/index.js +0 -10
  101. package/lib/value-parsers/json-value-parser.js +0 -7
  102. package/lib/value-parsers/number-value-parser.js +0 -9
  103. package/lib/value-parsers/string-value-parser.js +3 -20
  104. package/lib/value-parsers/to-many-value-parser.js +1 -42
  105. package/lib/value-parsers/to-one-value-parser.js +0 -14
  106. package/lib/view/field-type-map.d.ts +47 -0
  107. package/lib/view/field-type-map.js +56 -0
  108. package/lib/view/view-inference.d.ts +31 -0
  109. package/lib/view/view-inference.js +92 -0
  110. package/lib/view-collection.d.ts +6 -0
  111. package/lib/view-collection.js +24 -0
  112. package/package.json +4 -4
  113. package/src/__tests__/collection.test.ts +44 -0
  114. package/src/__tests__/fields/date.test.ts +75 -0
  115. package/src/__tests__/fields/sort-field.test.ts +100 -0
  116. package/src/__tests__/filter.test.ts +3 -3
  117. package/src/__tests__/group.test.ts +50 -0
  118. package/src/__tests__/inhertits/collection-inherits.test.ts +114 -0
  119. package/src/__tests__/operator/date-operator.test.ts +244 -98
  120. package/src/__tests__/operator/eq.test.ts +76 -0
  121. package/src/__tests__/operator/ne.test.ts +19 -1
  122. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +82 -0
  123. package/src/__tests__/repository/find.test.ts +33 -0
  124. package/src/__tests__/repository.test.ts +88 -0
  125. package/src/__tests__/sql-parser.test.ts +13 -0
  126. package/src/__tests__/tree.test.ts +217 -0
  127. package/src/__tests__/view/list-view.test.ts +34 -0
  128. package/src/__tests__/view/view-collection.test.ts +199 -0
  129. package/src/__tests__/view/view-inference.test.ts +145 -0
  130. package/src/__tests__/view/view-repository.test.ts +67 -0
  131. package/src/collection-group-manager.ts +94 -0
  132. package/src/collection.ts +108 -14
  133. package/src/database-utils/index.ts +1 -0
  134. package/src/database.ts +79 -12
  135. package/src/features/ReferencesMap.ts +3 -2
  136. package/src/fields/belongs-to-many-field.ts +15 -2
  137. package/src/fields/date-field.ts +18 -0
  138. package/src/fields/field.ts +16 -8
  139. package/src/fields/json-field.ts +1 -0
  140. package/src/fields/sort-field.ts +90 -29
  141. package/src/filter-parser.ts +1 -0
  142. package/src/index.ts +3 -1
  143. package/src/listeners/adjacency-list.ts +60 -0
  144. package/src/listeners/index.ts +7 -0
  145. package/src/mock-database.ts +14 -2
  146. package/src/model.ts +4 -0
  147. package/src/operators/child-collection.ts +24 -0
  148. package/src/operators/date.ts +108 -24
  149. package/src/operators/eq.ts +14 -0
  150. package/src/operators/index.ts +2 -0
  151. package/src/operators/ne.ts +12 -7
  152. package/src/options-parser.ts +25 -11
  153. package/src/query-interface/mysql-query-interface.ts +53 -1
  154. package/src/query-interface/postgres-query-interface.ts +84 -3
  155. package/src/query-interface/query-interface.ts +31 -0
  156. package/src/query-interface/sqlite-query-interface.ts +62 -1
  157. package/src/relation-repository/belongs-to-many-repository.ts +20 -1
  158. package/src/relation-repository/hasmany-repository.ts +5 -3
  159. package/src/relation-repository/multiple-relation-repository.ts +9 -1
  160. package/src/repository.ts +6 -13
  161. package/src/sql-parser/index.js +10698 -0
  162. package/src/sql-parser/readme.md +2 -0
  163. package/src/sql-parser/sql.pegjs +1297 -0
  164. package/src/sync-runner.ts +13 -15
  165. package/src/update-associations.ts +26 -22
  166. package/src/view/field-type-map.ts +56 -0
  167. package/src/view/view-inference.ts +106 -0
  168. package/src/view-collection.ts +21 -0
@@ -26,7 +26,7 @@ export class SyncRunner {
26
26
  );
27
27
  }
28
28
 
29
- const tableName = inheritedCollection.addSchemaTableName();
29
+ const tableName = inheritedCollection.getTableNameWithSchema();
30
30
 
31
31
  const attributes = model.tableAttributes;
32
32
 
@@ -44,7 +44,7 @@ export class SyncRunner {
44
44
  `SELECT column_default
45
45
  FROM information_schema.columns
46
46
  WHERE table_name = '${parent.model.tableName}'
47
- and table_schema = '${parent.collectionSchema()}'
47
+ and table_schema = '${parent.collectionSchema()}'
48
48
  and "column_name" = 'id';`,
49
49
  {
50
50
  transaction,
@@ -65,7 +65,6 @@ export class SyncRunner {
65
65
  const match = regex.exec(columnDefault);
66
66
 
67
67
  const sequenceName = match[1];
68
-
69
68
  const sequenceCurrentValResult = await queryInterface.sequelize.query(
70
69
  `select last_value
71
70
  from ${sequenceName}`,
@@ -88,7 +87,7 @@ export class SyncRunner {
88
87
  // if we have max sequence, set it to child table
89
88
  if (maxSequenceName) {
90
89
  const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map((parent) =>
91
- db.getCollection(parent).addSchemaTableName(),
90
+ db.getCollection(parent).getTableNameWithSchema(),
92
91
  );
93
92
 
94
93
  const sequenceTables = [...parentsDeep, tableName];
@@ -97,17 +96,16 @@ export class SyncRunner {
97
96
  const tableName = sequenceTable.tableName;
98
97
  const schemaName = sequenceTable.schema;
99
98
 
100
- const queryName = Boolean(tableName.match(/[A-Z]/)) && !tableName.includes(`"`) ? `"${tableName}"` : tableName;
99
+ const idColumnSql = `SELECT column_name
100
+ FROM information_schema.columns
101
+ WHERE table_name = '${tableName}'
102
+ and column_name = 'id'
103
+ and table_schema = '${schemaName}';
104
+ `;
101
105
 
102
- const idColumnQuery = await queryInterface.sequelize.query(
103
- `SELECT column_name
104
- FROM information_schema.columns
105
- WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schemaName}';
106
- `,
107
- {
108
- transaction,
109
- },
110
- );
106
+ const idColumnQuery = await queryInterface.sequelize.query(idColumnSql, {
107
+ transaction,
108
+ });
111
109
 
112
110
  if (idColumnQuery[0].length == 0) {
113
111
  continue;
@@ -163,7 +161,7 @@ WHERE table_name='${queryName}' and column_name='id' and table_schema = '${schem
163
161
  ';',
164
162
  ` INHERITS (${parents
165
163
  .map((t) => {
166
- return t.addSchemaTableName();
164
+ return t.getTableNameWithSchema();
167
165
  })
168
166
  .join(', ')});`,
169
167
  );
@@ -6,10 +6,11 @@ import {
6
6
  HasOne,
7
7
  Hookable,
8
8
  ModelStatic,
9
- Transactionable
9
+ Transactionable,
10
10
  } from 'sequelize';
11
11
  import { Model } from './model';
12
12
  import { UpdateGuard } from './update-guard';
13
+ import lodash from 'lodash';
13
14
 
14
15
  function isUndefinedOrNull(value: any) {
15
16
  return typeof value === 'undefined' || value === null;
@@ -396,38 +397,40 @@ export async function updateMultipleAssociation(
396
397
  return;
397
398
  }
398
399
 
399
- if (!Array.isArray(value)) {
400
- value = [value];
401
- }
400
+ value = lodash.castArray(value);
401
+
402
+ const setItems = []; // to be setted
403
+ const objectItems = []; // to be added
402
404
 
403
- const list1 = []; // to be setted
404
- const list2 = []; // to be added
405
- const created = [];
405
+ // iterate item in value
406
406
  for (const item of value) {
407
407
  if (isUndefinedOrNull(item)) {
408
408
  continue;
409
409
  }
410
+
410
411
  if (isStringOrNumber(item)) {
411
- list1.push(item);
412
+ setItems.push(item);
412
413
  } else if (item instanceof Model) {
413
- list1.push(item);
414
+ setItems.push(item);
414
415
  } else if (item.sequelize) {
415
- list1.push(item);
416
+ setItems.push(item);
416
417
  } else if (typeof item === 'object') {
417
418
  const targetKey = (association as any).targetKey || 'id';
419
+
418
420
  if (item[targetKey]) {
419
- created.push(item[targetKey]);
420
- list1.push(item[targetKey]);
421
+ setItems.push(item[targetKey]);
421
422
  }
422
- list2.push(item);
423
+
424
+ objectItems.push(item);
423
425
  }
424
426
  }
425
427
 
426
428
  // associate targets in lists1
427
- await model[setAccessor](list1, { transaction, context, individualHooks: true });
429
+ await model[setAccessor](setItems, { transaction, context, individualHooks: true });
428
430
 
429
- const list3 = [];
430
- for (const item of list2) {
431
+ const newItems = [];
432
+
433
+ for (const item of objectItems) {
431
434
  const pk = association.target.primaryKeyAttribute;
432
435
 
433
436
  const through = (<any>association).through ? (<any>association).through.model.name : null;
@@ -446,13 +449,14 @@ export async function updateMultipleAssociation(
446
449
  if (isUndefinedOrNull(item[pk])) {
447
450
  // create new record
448
451
  const instance = await model[createAccessor](item, accessorOptions);
452
+
449
453
  await updateAssociations(instance, item, {
450
454
  ...options,
451
455
  transaction,
452
456
  associationContext: association,
453
457
  updateAssociationValues: keys,
454
458
  });
455
- list3.push(instance);
459
+ newItems.push(instance);
456
460
  } else {
457
461
  // set & update record
458
462
  const instance = await association.target.findByPk<any>(item[pk], {
@@ -462,9 +466,9 @@ export async function updateMultipleAssociation(
462
466
  continue;
463
467
  }
464
468
  const addAccessor = association.accessors.add;
465
- if (!created.includes(item[pk])) {
466
- await model[addAccessor](item[pk], accessorOptions);
467
- }
469
+
470
+ await model[addAccessor](item[pk], accessorOptions);
471
+
468
472
  if (!recursive) {
469
473
  continue;
470
474
  }
@@ -477,11 +481,11 @@ export async function updateMultipleAssociation(
477
481
  associationContext: association,
478
482
  updateAssociationValues: keys,
479
483
  });
480
- list3.push(instance);
484
+ newItems.push(instance);
481
485
  }
482
486
  }
483
487
 
484
- model.setDataValue(key, list1.concat(list3));
488
+ model.setDataValue(key, setItems.concat(newItems));
485
489
  } catch (error) {
486
490
  throw error;
487
491
  }
@@ -0,0 +1,56 @@
1
+ const postgres = {
2
+ 'character varying': 'string',
3
+ varchar: 'string',
4
+ text: 'text',
5
+ char: 'string',
6
+
7
+ smallint: 'integer',
8
+ integer: 'integer',
9
+ bigint: 'bigInt',
10
+ decimal: 'float',
11
+ numeric: 'float',
12
+ 'double precision': 'float',
13
+
14
+ 'timestamp without time zone': 'date',
15
+ 'timestamp with time zone': 'date',
16
+ date: 'date',
17
+ boolean: 'boolean',
18
+
19
+ json: ['json', 'array'],
20
+ jsonb: ['jsonb', 'array'],
21
+ };
22
+
23
+ const mysql = {
24
+ varchar: 'string',
25
+ text: 'text',
26
+ int: 'integer',
27
+ integer: 'integer',
28
+ bigint: 'bigInt',
29
+ float: 'float',
30
+ double: 'float',
31
+ boolean: 'boolean',
32
+
33
+ tinyint: 'integer',
34
+ datetime: 'date',
35
+ timestamp: 'date',
36
+ json: ['json', 'array'],
37
+ };
38
+
39
+ const sqlite = {
40
+ text: 'text',
41
+ varchar: 'string',
42
+
43
+ integer: 'integer',
44
+ real: 'real',
45
+
46
+ datetime: 'date',
47
+ date: 'date',
48
+ time: 'time',
49
+
50
+ boolean: 'boolean',
51
+
52
+ numeric: 'decimal',
53
+ json: ['json', 'array'],
54
+ };
55
+
56
+ export default { postgres, mysql, sqlite };
@@ -0,0 +1,106 @@
1
+ import Database from '../database';
2
+ import FieldTypeMap from './field-type-map';
3
+ import { isArray } from 'mathjs';
4
+
5
+ type InferredField = {
6
+ name: string;
7
+ type: string;
8
+ source?: string;
9
+ };
10
+
11
+ type InferredFieldResult = {
12
+ [key: string]: InferredField;
13
+ };
14
+
15
+ export class ViewFieldInference {
16
+ static async inferFields(options: {
17
+ db: Database;
18
+ viewName: string;
19
+ viewSchema?: string;
20
+ }): Promise<InferredFieldResult> {
21
+ const { db } = options;
22
+ if (!db.inDialect('postgres')) {
23
+ options.viewSchema = undefined;
24
+ }
25
+
26
+ const columns = await db.sequelize.getQueryInterface().describeTable(options.viewName, options.viewSchema);
27
+
28
+ const columnUsage = await db.queryInterface.viewColumnUsage({
29
+ viewName: options.viewName,
30
+ schema: options.viewSchema,
31
+ });
32
+
33
+ // @ts-ignore
34
+ return Object.fromEntries(
35
+ Object.entries(columns).map(([name, column]) => {
36
+ const usage = columnUsage[name];
37
+
38
+ if (usage) {
39
+ const collectionField = (() => {
40
+ const tableName = `${usage.table_schema ? `${usage.table_schema}.` : ''}${usage.table_name}`;
41
+ const collection = db.tableNameCollectionMap.get(tableName);
42
+ if (!collection) return false;
43
+
44
+ const fieldValue = Object.values(collection.model.rawAttributes).find(
45
+ (field) => field.field === usage.column_name,
46
+ );
47
+
48
+ if (!fieldValue) {
49
+ return false;
50
+ }
51
+
52
+ // @ts-ignore
53
+ const fieldName = fieldValue?.fieldName;
54
+
55
+ return collection.getField(fieldName);
56
+ })();
57
+
58
+ if (collectionField && collectionField.options.interface) {
59
+ return [
60
+ name,
61
+ {
62
+ name,
63
+ type: collectionField.type,
64
+ source: `${collectionField.collection.name}.${collectionField.name}`,
65
+ },
66
+ ];
67
+ }
68
+ }
69
+
70
+ return [
71
+ name,
72
+ {
73
+ name,
74
+ ...this.inferToFieldType({ db, name, type: column.type }),
75
+ },
76
+ ];
77
+ }),
78
+ );
79
+ }
80
+
81
+ static inferToFieldType(options: { db: Database; name: string; type: string }) {
82
+ const { db } = options;
83
+ const dialect = db.sequelize.getDialect();
84
+ const fieldTypeMap = FieldTypeMap[dialect];
85
+
86
+ if (!options.type) {
87
+ return {
88
+ possibleTypes: Object.keys(fieldTypeMap),
89
+ };
90
+ }
91
+
92
+ const queryType = options.type.toLowerCase().replace(/\(\d+\)/, '');
93
+ const mappedType = fieldTypeMap[queryType];
94
+
95
+ if (isArray(mappedType)) {
96
+ return {
97
+ type: mappedType[0],
98
+ possibleTypes: mappedType,
99
+ };
100
+ }
101
+
102
+ return {
103
+ type: mappedType,
104
+ };
105
+ }
106
+ }
@@ -0,0 +1,21 @@
1
+ import { Collection, CollectionContext, CollectionOptions } from './collection';
2
+
3
+ export class ViewCollection extends Collection {
4
+ constructor(options: CollectionOptions, context: CollectionContext) {
5
+ options.autoGenId = false;
6
+ options.timestamps = false;
7
+ options.underscored = false;
8
+
9
+ super(options, context);
10
+ }
11
+
12
+ protected sequelizeModelOptions(): any {
13
+ const modelOptions = super.sequelizeModelOptions();
14
+ modelOptions.tableName = this.options.viewName || this.options.name;
15
+ return modelOptions;
16
+ }
17
+
18
+ isView() {
19
+ return true;
20
+ }
21
+ }