@nocobase/database 0.9.0-alpha.2 → 0.9.1-alpha.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 (103) hide show
  1. package/lib/collection-importer.js +1 -1
  2. package/lib/collection.d.ts +7 -1
  3. package/lib/collection.js +135 -61
  4. package/lib/database-utils/index.d.ts +8 -0
  5. package/lib/database-utils/index.js +59 -0
  6. package/lib/database.d.ts +26 -3
  7. package/lib/database.js +224 -61
  8. package/lib/fields/array-field.d.ts +1 -1
  9. package/lib/fields/array-field.js +2 -2
  10. package/lib/fields/belongs-to-many-field.js +1 -2
  11. package/lib/fields/field.d.ts +1 -0
  12. package/lib/fields/field.js +37 -15
  13. package/lib/fields/has-one-field.d.ts +1 -1
  14. package/lib/fields/has-one-field.js +9 -5
  15. package/lib/fields/number-field.d.ts +9 -6
  16. package/lib/fields/number-field.js +8 -6
  17. package/lib/fields/sort-field.js +15 -1
  18. package/lib/index.d.ts +6 -4
  19. package/lib/index.js +59 -36
  20. package/lib/mock-database.d.ts +2 -0
  21. package/lib/mock-database.js +3 -1
  22. package/lib/model.js +10 -1
  23. package/lib/options-parser.js +3 -0
  24. package/lib/relation-repository/belongs-to-many-repository.js +4 -2
  25. package/lib/repository.js +5 -2
  26. package/lib/sync-runner.d.ts +1 -1
  27. package/lib/sync-runner.js +28 -18
  28. package/lib/types.d.ts +7 -1
  29. package/lib/update-associations.js +17 -3
  30. package/lib/update-guard.d.ts +1 -0
  31. package/lib/update-guard.js +6 -0
  32. package/lib/utils.d.ts +5 -0
  33. package/lib/utils.js +68 -0
  34. package/lib/value-parsers/array-value-parser.d.ts +8 -0
  35. package/lib/value-parsers/array-value-parser.js +76 -0
  36. package/lib/value-parsers/base-value-parser.d.ts +12 -0
  37. package/lib/value-parsers/base-value-parser.js +59 -0
  38. package/lib/value-parsers/boolean-value-parser.d.ts +4 -0
  39. package/lib/value-parsers/boolean-value-parser.js +46 -0
  40. package/lib/value-parsers/date-value-parser.d.ts +5 -0
  41. package/lib/value-parsers/date-value-parser.js +91 -0
  42. package/lib/value-parsers/index.d.ts +12 -0
  43. package/lib/value-parsers/index.js +102 -0
  44. package/lib/value-parsers/json-value-parser.d.ts +4 -0
  45. package/lib/value-parsers/json-value-parser.js +37 -0
  46. package/lib/value-parsers/number-value-parser.d.ts +4 -0
  47. package/lib/value-parsers/number-value-parser.js +49 -0
  48. package/lib/value-parsers/string-value-parser.d.ts +8 -0
  49. package/lib/value-parsers/string-value-parser.js +76 -0
  50. package/lib/value-parsers/to-many-value-parser.d.ts +13 -0
  51. package/lib/value-parsers/to-many-value-parser.js +169 -0
  52. package/lib/value-parsers/to-one-value-parser.d.ts +4 -0
  53. package/lib/value-parsers/to-one-value-parser.js +49 -0
  54. package/package.json +4 -3
  55. package/src/__tests__/bigint.test.ts +1 -1
  56. package/src/__tests__/collection-importer.test.ts +13 -1
  57. package/src/__tests__/collection.test.ts +19 -9
  58. package/src/__tests__/database.test.ts +32 -0
  59. package/src/__tests__/fields/sort-field.test.ts +23 -0
  60. package/src/__tests__/inhertits/collection-inherits.test.ts +7 -5
  61. package/src/__tests__/percent2float.test.ts +14 -0
  62. package/src/__tests__/postgres/schema.test.ts +120 -0
  63. package/src/__tests__/underscored-options.test.ts +207 -0
  64. package/src/__tests__/update-associations-through.test.ts +73 -0
  65. package/src/__tests__/value-parsers/base.test.ts +20 -0
  66. package/src/__tests__/value-parsers/date.test.ts +67 -0
  67. package/src/__tests__/value-parsers/number.test.ts +46 -0
  68. package/src/__tests__/value-parsers/to-many.test.ts +206 -0
  69. package/src/__tests__/value-parsers/to-one.test.ts +60 -0
  70. package/src/collection-importer.ts +2 -2
  71. package/src/collection.ts +97 -15
  72. package/src/database-utils/index.ts +38 -0
  73. package/src/database.ts +171 -33
  74. package/src/fields/array-field.ts +1 -1
  75. package/src/fields/belongs-to-field.ts +1 -1
  76. package/src/fields/belongs-to-many-field.ts +0 -1
  77. package/src/fields/field.ts +45 -16
  78. package/src/fields/has-many-field.ts +1 -1
  79. package/src/fields/has-one-field.ts +11 -7
  80. package/src/fields/number-field.ts +10 -6
  81. package/src/fields/sort-field.ts +13 -1
  82. package/src/index.ts +7 -4
  83. package/src/inherited-collection.ts +1 -0
  84. package/src/mock-database.ts +3 -1
  85. package/src/model.ts +11 -2
  86. package/src/options-parser.ts +5 -0
  87. package/src/relation-repository/belongs-to-many-repository.ts +4 -2
  88. package/src/repository.ts +8 -3
  89. package/src/sync-runner.ts +33 -19
  90. package/src/types.ts +12 -1
  91. package/src/update-associations.ts +12 -5
  92. package/src/update-guard.ts +6 -0
  93. package/src/utils.ts +94 -0
  94. package/src/value-parsers/array-value-parser.ts +30 -0
  95. package/src/value-parsers/base-value-parser.ts +40 -0
  96. package/src/value-parsers/boolean-value-parser.ts +29 -0
  97. package/src/value-parsers/date-value-parser.ts +38 -0
  98. package/src/value-parsers/index.ts +46 -0
  99. package/src/value-parsers/json-value-parser.ts +19 -0
  100. package/src/value-parsers/number-value-parser.ts +29 -0
  101. package/src/value-parsers/string-value-parser.ts +31 -0
  102. package/src/value-parsers/to-many-value-parser.ts +85 -0
  103. package/src/value-parsers/to-one-value-parser.ts +20 -0
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.StringValueParser = void 0;
7
+
8
+ var _baseValueParser = require("./base-value-parser");
9
+
10
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
11
+
12
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
13
+
14
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
15
+
16
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
17
+
18
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
19
+
20
+ class StringValueParser extends _baseValueParser.BaseValueParser {
21
+ setValue(value) {
22
+ var _this = this;
23
+
24
+ return _asyncToGenerator(function* () {
25
+ const _this$getOptions = _this.getOptions(),
26
+ map = _this$getOptions.map,
27
+ set = _this$getOptions.set;
28
+
29
+ if (set.size > 0) {
30
+ if (map.has(value)) {
31
+ value = map.get(value);
32
+ }
33
+
34
+ if (set.has(value)) {
35
+ _this.value = value;
36
+ } else {
37
+ _this.errors.push(`No matching option found - ${JSON.stringify(value)}`);
38
+ }
39
+ } else {
40
+ _this.value = value;
41
+ }
42
+ })();
43
+ }
44
+
45
+ getOptions() {
46
+ var _this$field$options, _this$field$options$u;
47
+
48
+ const options = ((_this$field$options = this.field.options) === null || _this$field$options === void 0 ? void 0 : (_this$field$options$u = _this$field$options['uiSchema']) === null || _this$field$options$u === void 0 ? void 0 : _this$field$options$u.enum) || [];
49
+ const map = new Map();
50
+ const set = new Set();
51
+
52
+ var _iterator = _createForOfIteratorHelper(options),
53
+ _step;
54
+
55
+ try {
56
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
57
+ const option = _step.value;
58
+ set.add(option.value);
59
+ set.add(option.label);
60
+ map.set(option.label, option.value);
61
+ }
62
+ } catch (err) {
63
+ _iterator.e(err);
64
+ } finally {
65
+ _iterator.f();
66
+ }
67
+
68
+ return {
69
+ map,
70
+ set
71
+ };
72
+ }
73
+
74
+ }
75
+
76
+ exports.StringValueParser = StringValueParser;
@@ -0,0 +1,13 @@
1
+ import { BaseValueParser } from './base-value-parser';
2
+ export declare class ToManyValueParser extends BaseValueParser {
3
+ setAccessors: {
4
+ attachment: string;
5
+ chinaRegion: string;
6
+ };
7
+ setAttachments(value: any): Promise<void>;
8
+ setChinaRegion(value: any): Promise<void>;
9
+ setAssociations(value: any): Promise<void>;
10
+ setValue(value: any): Promise<void>;
11
+ getInterface(): string;
12
+ isInterface(name: string): boolean;
13
+ }
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ToManyValueParser = void 0;
7
+
8
+ function _path() {
9
+ const data = require("path");
10
+
11
+ _path = function _path() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ var _baseValueParser = require("./base-value-parser");
19
+
20
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
21
+
22
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
23
+
24
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
25
+
26
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
27
+
28
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
29
+
30
+ class ToManyValueParser extends _baseValueParser.BaseValueParser {
31
+ constructor(...args) {
32
+ super(...args);
33
+ this.setAccessors = {
34
+ attachment: 'setAttachments',
35
+ chinaRegion: 'setChinaRegion'
36
+ };
37
+ }
38
+
39
+ setAttachments(value) {
40
+ var _this = this;
41
+
42
+ return _asyncToGenerator(function* () {
43
+ _this.value = _this.toArr(value).map(url => {
44
+ return {
45
+ title: (0, _path().basename)(url),
46
+ extname: (0, _path().extname)(url),
47
+ filename: (0, _path().basename)(url),
48
+ url
49
+ };
50
+ });
51
+ })();
52
+ }
53
+
54
+ setChinaRegion(value) {
55
+ var _this2 = this;
56
+
57
+ return _asyncToGenerator(function* () {
58
+ const repository = _this2.field.database.getRepository(_this2.field.target);
59
+
60
+ try {
61
+ const values = [];
62
+
63
+ const names = _this2.toArr(value, '/');
64
+
65
+ let parentCode = null;
66
+
67
+ var _iterator = _createForOfIteratorHelper(names),
68
+ _step;
69
+
70
+ try {
71
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
72
+ const name = _step.value;
73
+ const instance = yield repository.findOne({
74
+ filter: {
75
+ name: name.trim(),
76
+ parentCode
77
+ }
78
+ });
79
+
80
+ if (!instance) {
81
+ throw new Error(`"${value}" does not exist`);
82
+ }
83
+
84
+ parentCode = instance.get('code');
85
+ values.push(parentCode);
86
+ }
87
+ } catch (err) {
88
+ _iterator.e(err);
89
+ } finally {
90
+ _iterator.f();
91
+ }
92
+
93
+ if (values.length !== names.length) {
94
+ throw new Error(`"${value}" does not exist`);
95
+ }
96
+
97
+ _this2.value = values;
98
+ } catch (error) {
99
+ _this2.errors.push(error.message);
100
+ }
101
+ })();
102
+ }
103
+
104
+ setAssociations(value) {
105
+ var _this3 = this;
106
+
107
+ return _asyncToGenerator(function* () {
108
+ var _this3$ctx, _this3$ctx$column;
109
+
110
+ const dataIndex = ((_this3$ctx = _this3.ctx) === null || _this3$ctx === void 0 ? void 0 : (_this3$ctx$column = _this3$ctx.column) === null || _this3$ctx$column === void 0 ? void 0 : _this3$ctx$column.dataIndex) || [];
111
+
112
+ if (Array.isArray(dataIndex) && dataIndex.length < 2) {
113
+ _this3.errors.push(`data index invalid`);
114
+
115
+ return;
116
+ }
117
+
118
+ const key = _this3.ctx.column.dataIndex[1];
119
+
120
+ const repository = _this3.field.database.getRepository(_this3.field.target);
121
+
122
+ try {
123
+ _this3.value = yield Promise.all(_this3.toArr(value).map( /*#__PURE__*/function () {
124
+ var _ref = _asyncToGenerator(function* (v) {
125
+ const instance = yield repository.findOne({
126
+ filter: {
127
+ [key]: v
128
+ }
129
+ });
130
+
131
+ if (!instance) {
132
+ throw new Error(`"${v}" does not exist`);
133
+ }
134
+
135
+ return instance.get(_this3.field.targetKey || 'id');
136
+ });
137
+
138
+ return function (_x) {
139
+ return _ref.apply(this, arguments);
140
+ };
141
+ }()));
142
+ } catch (error) {
143
+ _this3.errors.push(error.message);
144
+ }
145
+ })();
146
+ }
147
+
148
+ setValue(value) {
149
+ var _this4 = this;
150
+
151
+ return _asyncToGenerator(function* () {
152
+ const setAccessor = _this4.setAccessors[_this4.getInterface()] || 'setAssociations';
153
+ yield _this4[setAccessor](value);
154
+ })();
155
+ }
156
+
157
+ getInterface() {
158
+ var _this$field, _this$field$options;
159
+
160
+ return (_this$field = this.field) === null || _this$field === void 0 ? void 0 : (_this$field$options = _this$field.options) === null || _this$field$options === void 0 ? void 0 : _this$field$options.interface;
161
+ }
162
+
163
+ isInterface(name) {
164
+ return this.getInterface() === name;
165
+ }
166
+
167
+ }
168
+
169
+ exports.ToManyValueParser = ToManyValueParser;
@@ -0,0 +1,4 @@
1
+ import { BaseValueParser } from './base-value-parser';
2
+ export declare class ToOneValueParser extends BaseValueParser {
3
+ setValue(value: any): Promise<void>;
4
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ToOneValueParser = void 0;
7
+
8
+ var _baseValueParser = require("./base-value-parser");
9
+
10
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
11
+
12
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
13
+
14
+ class ToOneValueParser extends _baseValueParser.BaseValueParser {
15
+ setValue(value) {
16
+ var _this = this;
17
+
18
+ return _asyncToGenerator(function* () {
19
+ var _this$ctx, _this$ctx$column;
20
+
21
+ const dataIndex = ((_this$ctx = _this.ctx) === null || _this$ctx === void 0 ? void 0 : (_this$ctx$column = _this$ctx.column) === null || _this$ctx$column === void 0 ? void 0 : _this$ctx$column.dataIndex) || [];
22
+
23
+ if (Array.isArray(dataIndex) && dataIndex.length < 2) {
24
+ _this.errors.push(`data index invalid`);
25
+
26
+ return;
27
+ }
28
+
29
+ const key = _this.ctx.column.dataIndex[1];
30
+
31
+ const repository = _this.field.database.getRepository(_this.field.target);
32
+
33
+ const instance = yield repository.findOne({
34
+ filter: {
35
+ [key]: _this.trim(value)
36
+ }
37
+ });
38
+
39
+ if (instance) {
40
+ _this.value = instance.get(_this.field.targetKey || 'id');
41
+ } else {
42
+ _this.errors.push(`"${value}" does not exist`);
43
+ }
44
+ })();
45
+ }
46
+
47
+ }
48
+
49
+ exports.ToOneValueParser = ToOneValueParser;
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "0.9.0-alpha.2",
3
+ "version": "0.9.1-alpha.1",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "license": "Apache-2.0",
8
8
  "dependencies": {
9
- "@nocobase/utils": "0.9.0-alpha.2",
9
+ "@nocobase/utils": "0.9.1-alpha.1",
10
10
  "async-mutex": "^0.3.2",
11
11
  "cron-parser": "4.4.0",
12
12
  "deepmerge": "^4.2.2",
13
+ "excel-date-to-js": "^1.1.5",
13
14
  "flat": "^5.0.2",
14
15
  "glob": "^7.1.6",
15
16
  "mathjs": "^10.6.1",
@@ -26,5 +27,5 @@
26
27
  "url": "git+https://github.com/nocobase/nocobase.git",
27
28
  "directory": "packages/database"
28
29
  },
29
- "gitHead": "b8f76ad38e60e677c5bb4aab0a4cdb28d98a0f49"
30
+ "gitHead": "56cb184b00dc383b853015d525bf6e79dea92169"
30
31
  }
@@ -43,6 +43,6 @@ excludeSqlite()('collection', () => {
43
43
 
44
44
  const profileTableInfo = await db.sequelize.getQueryInterface().describeTable(profile.model.tableName);
45
45
 
46
- expect(profileTableInfo['userId'].type).toBe('BIGINT');
46
+ expect(profileTableInfo[profile.model.rawAttributes['userId'].field].type).toBe('BIGINT');
47
47
  });
48
48
  });
@@ -7,7 +7,19 @@ describe('collection importer', () => {
7
7
  const reader = new ImporterReader(path.resolve(__dirname, './fixtures/collections'));
8
8
 
9
9
  const modules = await reader.read();
10
- expect(modules).toBeDefined();
10
+
11
+ const posts = modules.find((m) => m.name === 'posts');
12
+
13
+ expect(posts).toMatchObject({
14
+ name: 'posts',
15
+ fields: [{ type: 'string', name: 'title' }],
16
+ });
17
+
18
+ posts.schema = 'test';
19
+
20
+ const modules2 = await reader.read();
21
+ const posts2 = modules2.find((m) => m.name === 'posts');
22
+ expect(posts2.schema).toBeFalsy();
11
23
  });
12
24
 
13
25
  test('extend', async () => {
@@ -8,9 +8,7 @@ describe('collection', () => {
8
8
  let db: Database;
9
9
 
10
10
  beforeEach(async () => {
11
- db = mockDatabase({
12
- logging: console.log,
13
- });
11
+ db = mockDatabase();
14
12
 
15
13
  await db.clean({ drop: true });
16
14
  });
@@ -240,9 +238,15 @@ describe('collection sync', () => {
240
238
  await collection.sync();
241
239
  const tableFields = await (<any>collection.model).queryInterface.describeTable(`${db.getTablePrefix()}users`);
242
240
 
243
- expect(tableFields).toHaveProperty('firstName');
244
- expect(tableFields).toHaveProperty('lastName');
245
- expect(tableFields).toHaveProperty('age');
241
+ if (db.options.underscored) {
242
+ expect(tableFields).toHaveProperty('first_name');
243
+ expect(tableFields).toHaveProperty('last_name');
244
+ expect(tableFields).toHaveProperty('age');
245
+ } else {
246
+ expect(tableFields).toHaveProperty('firstName');
247
+ expect(tableFields).toHaveProperty('lastName');
248
+ expect(tableFields).toHaveProperty('age');
249
+ }
246
250
  });
247
251
 
248
252
  test('sync with association not exists', async () => {
@@ -290,9 +294,15 @@ describe('collection sync', () => {
290
294
 
291
295
  const model = collection.model;
292
296
  await collection.sync();
293
- const tableFields = await (<any>model).queryInterface.describeTable(`${db.getTablePrefix()}postsTags`);
294
- expect(tableFields['postId']).toBeDefined();
295
- expect(tableFields['tagId']).toBeDefined();
297
+ if (db.options.underscored) {
298
+ const tableFields = await (<any>model).queryInterface.describeTable(`${db.getTablePrefix()}posts_tags`);
299
+ expect(tableFields['post_id']).toBeDefined();
300
+ expect(tableFields['tag_id']).toBeDefined();
301
+ } else {
302
+ const tableFields = await (<any>model).queryInterface.describeTable(`${db.getTablePrefix()}postsTags`);
303
+ expect(tableFields['postId']).toBeDefined();
304
+ expect(tableFields['tagId']).toBeDefined();
305
+ }
296
306
  });
297
307
 
298
308
  test('limit table name length', async () => {
@@ -285,4 +285,36 @@ describe('database', () => {
285
285
  test.customMethod();
286
286
  expect(test.get('abc')).toBe('abc');
287
287
  });
288
+
289
+ test('getFieldByPath', () => {
290
+ db.collection({
291
+ name: 'users',
292
+ fields: [{ type: 'hasMany', name: 'p', target: 'posts' }],
293
+ });
294
+ db.collection({
295
+ name: 'comments',
296
+ fields: [{ type: 'string', name: 'title' }],
297
+ });
298
+ db.collection({
299
+ name: 'posts',
300
+ fields: [
301
+ { type: 'hasMany', name: 'c', target: 'comments' },
302
+ { type: 'belongsToMany', name: 't', target: 'tags' },
303
+ ],
304
+ });
305
+ db.collection({
306
+ name: 'tags',
307
+ fields: [{ type: 'string', name: 'title' }],
308
+ });
309
+ const f1 = db.getFieldByPath('users.p.t');
310
+ const f2 = db.getFieldByPath('users.p.c');
311
+ const f3 = db.getFieldByPath('users.p');
312
+ expect(f1.name).toBe('t');
313
+ expect(f2.name).toBe('c');
314
+ expect(f3.name).toBe('p');
315
+ expect(db.getFieldByPath('users.d')).toBeNull;
316
+ expect(db.getFieldByPath('users.d.e')).toBeNull;
317
+ expect(db.getFieldByPath('users.p.f')).toBeNull;
318
+ expect(db.getFieldByPath('users.p.c.j')).toBeNull;
319
+ });
288
320
  });
@@ -17,6 +17,29 @@ describe('string field', () => {
17
17
  await db.close();
18
18
  });
19
19
 
20
+ it('should init sorted value by createdAt when primaryKey not exists', async () => {
21
+ const Test = db.collection({
22
+ autoGenId: false,
23
+ name: 'tests',
24
+ });
25
+
26
+ await db.sync();
27
+ await Test.repository.create({
28
+ values: [{}, {}, {}],
29
+ });
30
+
31
+ // add sort field
32
+ Test.setField('sort', { type: 'sort' });
33
+
34
+ let err;
35
+ try {
36
+ await db.sync();
37
+ } catch (e) {
38
+ err = e;
39
+ }
40
+ expect(err).toBeFalsy();
41
+ });
42
+
20
43
  it('sort', async () => {
21
44
  const Test = db.collection({
22
45
  name: 'tests',
@@ -886,11 +886,13 @@ pgOnly()('collection inherits', () => {
886
886
 
887
887
  const studentTableInfo = await db.sequelize.getQueryInterface().describeTable(student.model.tableName);
888
888
 
889
- expect(studentTableInfo.score).toBeDefined();
890
- expect(studentTableInfo.name).toBeDefined();
891
- expect(studentTableInfo.id).toBeDefined();
892
- expect(studentTableInfo.createdAt).toBeDefined();
893
- expect(studentTableInfo.updatedAt).toBeDefined();
889
+ const getField = (name) => student.model.rawAttributes[name].field;
890
+
891
+ expect(studentTableInfo[getField('score')]).toBeDefined();
892
+ expect(studentTableInfo[getField('name')]).toBeDefined();
893
+ expect(studentTableInfo[getField('id')]).toBeDefined();
894
+ expect(studentTableInfo[getField('createdAt')]).toBeDefined();
895
+ expect(studentTableInfo[getField('updatedAt')]).toBeDefined();
894
896
  });
895
897
 
896
898
  it('should get parent fields', async () => {
@@ -0,0 +1,14 @@
1
+ import { percent2float } from '../utils';
2
+
3
+ describe('percent2float', () => {
4
+ it('should be NaN', () => {
5
+ expect(percent2float('123')).toBe(NaN);
6
+ expect(percent2float('3a')).toBe(NaN);
7
+ expect(percent2float('3a%')).toBe(NaN);
8
+ });
9
+
10
+ it('should be a floating point number', () => {
11
+ expect(percent2float('123%')).toBe(1.23);
12
+ expect(percent2float('22.5507%')).toBe(0.225507); // not 0.22550699999999999
13
+ });
14
+ });
@@ -0,0 +1,120 @@
1
+ import { mockDatabase } from '../index';
2
+ import { Database } from '../../database';
3
+ import { randomStr } from '@nocobase/test';
4
+
5
+ describe('auth', () => {
6
+ let db: Database;
7
+ afterEach(async () => {
8
+ if (db) {
9
+ await db.close();
10
+ }
11
+ });
12
+
13
+ it('should auto create schema on prepare when schema missing', async () => {
14
+ const schemaName = randomStr();
15
+
16
+ db = mockDatabase({
17
+ schema: schemaName,
18
+ });
19
+
20
+ if (!db.inDialect('postgres')) return;
21
+
22
+ const querySchemaExists = async () =>
23
+ await db.sequelize.query(
24
+ `SELECT schema_name
25
+ FROM information_schema.schemata
26
+ WHERE schema_name = '${schemaName}';`,
27
+ );
28
+
29
+ expect((await querySchemaExists())[0].length).toEqual(0);
30
+ await db.prepare();
31
+ expect((await querySchemaExists())[0].length).toEqual(1);
32
+ });
33
+ });
34
+
35
+ describe('postgres schema', () => {
36
+ let db: Database;
37
+
38
+ beforeEach(async () => {
39
+ db = mockDatabase({
40
+ schema: 'test_schema',
41
+ });
42
+
43
+ if (!db.inDialect('postgres')) return;
44
+ });
45
+
46
+ afterEach(async () => {
47
+ if (db.inDialect('postgres')) {
48
+ await db.sequelize.query(`DROP SCHEMA IF EXISTS ${db.options.schema} CASCADE;`);
49
+ }
50
+ await db.close();
51
+ });
52
+
53
+ it('should drop all tables in schemas', async () => {
54
+ if (!db.inDialect('postgres')) return;
55
+
56
+ const collection = db.collection({
57
+ name: 'test',
58
+ });
59
+
60
+ await db.sync();
61
+
62
+ await db.clean({ drop: true });
63
+
64
+ const tableInfo = await db.sequelize.query(
65
+ `SELECT *
66
+ FROM information_schema.tables
67
+ where table_schema = '${db.options.schema}'`,
68
+ );
69
+
70
+ expect(tableInfo[0].length).toEqual(0);
71
+ });
72
+
73
+ it('should support database schema option', async () => {
74
+ if (!db.inDialect('postgres')) return;
75
+
76
+ await db.clean({ drop: true });
77
+
78
+ const tableInfo = await db.sequelize.query(
79
+ `SELECT *
80
+ FROM information_schema.tables
81
+ where table_schema = '${db.options.schema}'`,
82
+ );
83
+
84
+ expect(tableInfo[0].length).toEqual(0);
85
+
86
+ const collection = db.collection({
87
+ name: 'test',
88
+ });
89
+
90
+ await db.sync();
91
+
92
+ const newTableInfo = await db.sequelize.query(
93
+ `SELECT *
94
+ FROM information_schema.tables
95
+ where table_schema = '${db.options.schema}'`,
96
+ );
97
+
98
+ expect(newTableInfo[0].find((item) => item['table_name'] == collection.model.tableName)).toBeTruthy();
99
+ });
100
+
101
+ it('should update schema options', async () => {
102
+ if (!db.inDialect('postgres')) return;
103
+
104
+ await db.clean({ drop: true });
105
+
106
+ const collection = db.collection({
107
+ name: 'test',
108
+ });
109
+
110
+ await db.sync();
111
+
112
+ collection.updateOptions({
113
+ ...collection.options,
114
+ schema: 'test',
115
+ });
116
+
117
+ // @ts-ignore
118
+ expect(collection.model._schema).toEqual('test');
119
+ });
120
+ });