@nocobase/database 0.9.0-alpha.2 → 0.9.1-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 (125) hide show
  1. package/lib/collection-importer.js +1 -1
  2. package/lib/collection.d.ts +9 -2
  3. package/lib/collection.js +153 -63
  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 +32 -3
  7. package/lib/database.js +237 -64
  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 +8 -3
  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/filter-parser.js +1 -1
  19. package/lib/index.d.ts +6 -4
  20. package/lib/index.js +59 -36
  21. package/lib/mock-database.d.ts +2 -0
  22. package/lib/mock-database.js +3 -1
  23. package/lib/model.js +10 -1
  24. package/lib/options-parser.js +3 -0
  25. package/lib/query-interface/mysql-query-interface.d.ts +7 -0
  26. package/lib/query-interface/mysql-query-interface.js +39 -0
  27. package/lib/query-interface/postgres-query-interface.d.ts +6 -0
  28. package/lib/query-interface/postgres-query-interface.js +41 -0
  29. package/lib/query-interface/query-interface-builder.d.ts +2 -0
  30. package/lib/query-interface/query-interface-builder.js +23 -0
  31. package/lib/query-interface/query-interface.d.ts +9 -0
  32. package/lib/query-interface/query-interface.js +18 -0
  33. package/lib/query-interface/sqlite-query-interface.d.ts +6 -0
  34. package/lib/query-interface/sqlite-query-interface.js +38 -0
  35. package/lib/relation-repository/belongs-to-many-repository.js +4 -2
  36. package/lib/relation-repository/multiple-relation-repository.js +2 -0
  37. package/lib/relation-repository/single-relation-repository.js +1 -0
  38. package/lib/repository.js +5 -2
  39. package/lib/sync-runner.d.ts +1 -1
  40. package/lib/sync-runner.js +29 -22
  41. package/lib/types.d.ts +7 -1
  42. package/lib/update-associations.js +17 -3
  43. package/lib/update-guard.d.ts +1 -0
  44. package/lib/update-guard.js +6 -0
  45. package/lib/utils.d.ts +5 -0
  46. package/lib/utils.js +78 -0
  47. package/lib/value-parsers/array-value-parser.d.ts +8 -0
  48. package/lib/value-parsers/array-value-parser.js +76 -0
  49. package/lib/value-parsers/base-value-parser.d.ts +12 -0
  50. package/lib/value-parsers/base-value-parser.js +59 -0
  51. package/lib/value-parsers/boolean-value-parser.d.ts +4 -0
  52. package/lib/value-parsers/boolean-value-parser.js +46 -0
  53. package/lib/value-parsers/date-value-parser.d.ts +5 -0
  54. package/lib/value-parsers/date-value-parser.js +91 -0
  55. package/lib/value-parsers/index.d.ts +12 -0
  56. package/lib/value-parsers/index.js +102 -0
  57. package/lib/value-parsers/json-value-parser.d.ts +4 -0
  58. package/lib/value-parsers/json-value-parser.js +37 -0
  59. package/lib/value-parsers/number-value-parser.d.ts +4 -0
  60. package/lib/value-parsers/number-value-parser.js +49 -0
  61. package/lib/value-parsers/string-value-parser.d.ts +8 -0
  62. package/lib/value-parsers/string-value-parser.js +76 -0
  63. package/lib/value-parsers/to-many-value-parser.d.ts +13 -0
  64. package/lib/value-parsers/to-many-value-parser.js +169 -0
  65. package/lib/value-parsers/to-one-value-parser.d.ts +4 -0
  66. package/lib/value-parsers/to-one-value-parser.js +49 -0
  67. package/package.json +5 -3
  68. package/src/__tests__/bigint.test.ts +1 -1
  69. package/src/__tests__/collection-importer.test.ts +13 -1
  70. package/src/__tests__/collection.test.ts +19 -9
  71. package/src/__tests__/database.test.ts +32 -0
  72. package/src/__tests__/fields/sort-field.test.ts +23 -0
  73. package/src/__tests__/filter.test.ts +60 -0
  74. package/src/__tests__/inhertits/collection-inherits.test.ts +7 -5
  75. package/src/__tests__/percent2float.test.ts +14 -0
  76. package/src/__tests__/postgres/schema.test.ts +120 -0
  77. package/src/__tests__/underscored-options.test.ts +207 -0
  78. package/src/__tests__/update-associations-through.test.ts +73 -0
  79. package/src/__tests__/value-parsers/base.test.ts +20 -0
  80. package/src/__tests__/value-parsers/date.test.ts +67 -0
  81. package/src/__tests__/value-parsers/number.test.ts +46 -0
  82. package/src/__tests__/value-parsers/to-many.test.ts +206 -0
  83. package/src/__tests__/value-parsers/to-one.test.ts +60 -0
  84. package/src/collection-importer.ts +2 -2
  85. package/src/collection.ts +115 -17
  86. package/src/database-utils/index.ts +38 -0
  87. package/src/database.ts +188 -36
  88. package/src/fields/array-field.ts +1 -1
  89. package/src/fields/belongs-to-field.ts +1 -1
  90. package/src/fields/belongs-to-many-field.ts +8 -3
  91. package/src/fields/field.ts +48 -17
  92. package/src/fields/has-many-field.ts +1 -1
  93. package/src/fields/has-one-field.ts +11 -7
  94. package/src/fields/number-field.ts +10 -6
  95. package/src/fields/sort-field.ts +13 -1
  96. package/src/filter-parser.ts +1 -1
  97. package/src/index.ts +7 -4
  98. package/src/inherited-collection.ts +1 -0
  99. package/src/mock-database.ts +3 -1
  100. package/src/model.ts +11 -2
  101. package/src/options-parser.ts +5 -0
  102. package/src/query-interface/mysql-query-interface.ts +20 -0
  103. package/src/query-interface/postgres-query-interface.ts +22 -0
  104. package/src/query-interface/query-interface-builder.ts +14 -0
  105. package/src/query-interface/query-interface.ts +12 -0
  106. package/src/query-interface/sqlite-query-interface.ts +18 -0
  107. package/src/relation-repository/belongs-to-many-repository.ts +4 -2
  108. package/src/relation-repository/multiple-relation-repository.ts +4 -0
  109. package/src/relation-repository/single-relation-repository.ts +2 -0
  110. package/src/repository.ts +8 -3
  111. package/src/sync-runner.ts +35 -24
  112. package/src/types.ts +12 -1
  113. package/src/update-associations.ts +12 -5
  114. package/src/update-guard.ts +6 -0
  115. package/src/utils.ts +95 -0
  116. package/src/value-parsers/array-value-parser.ts +30 -0
  117. package/src/value-parsers/base-value-parser.ts +40 -0
  118. package/src/value-parsers/boolean-value-parser.ts +29 -0
  119. package/src/value-parsers/date-value-parser.ts +38 -0
  120. package/src/value-parsers/index.ts +46 -0
  121. package/src/value-parsers/json-value-parser.ts +19 -0
  122. package/src/value-parsers/number-value-parser.ts +29 -0
  123. package/src/value-parsers/string-value-parser.ts +31 -0
  124. package/src/value-parsers/to-many-value-parser.ts +85 -0
  125. package/src/value-parsers/to-one-value-parser.ts +20 -0
@@ -0,0 +1,73 @@
1
+ import { Database } from '../database';
2
+ import { mockDatabase } from './';
3
+
4
+ describe('update through', () => {
5
+ let db: Database;
6
+ beforeEach(async () => {
7
+ db = mockDatabase();
8
+ await db.clean({ drop: true });
9
+ });
10
+
11
+ afterEach(async () => {
12
+ await db.close();
13
+ });
14
+
15
+ it('should not be reset', async () => {
16
+ db.collection({
17
+ name: 'c',
18
+ autoGenId: true,
19
+ fields: [
20
+ {
21
+ name: 'id',
22
+ type: 'integer',
23
+ primaryKey: true,
24
+ autoIncrement: true,
25
+ },
26
+ ],
27
+ });
28
+ db.collection({
29
+ name: 'a',
30
+ fields: [
31
+ {
32
+ type: 'string',
33
+ name: 'name',
34
+ },
35
+ {
36
+ type: 'belongsToMany',
37
+ name: 'b',
38
+ target: 'b',
39
+ through: 'c',
40
+ },
41
+ ],
42
+ });
43
+ db.collection({
44
+ name: 'b',
45
+ fields: [],
46
+ });
47
+ await db.sync();
48
+ const callback1 = jest.fn();
49
+ const callback2 = jest.fn();
50
+ db.on('c.afterCreate', callback1);
51
+ db.on('c.afterBulkCreate', callback2);
52
+ const b = await db.getRepository('b').create({
53
+ values: {},
54
+ });
55
+ const a = await db.getRepository('a').create({
56
+ values: {
57
+ b: [b.toJSON()],
58
+ },
59
+ });
60
+ const c1 = await db.getRepository('c').findOne();
61
+ await db.getRepository('a').update({
62
+ filterByTk: a.id,
63
+ values: {
64
+ b: [b.toJSON()],
65
+ },
66
+ });
67
+
68
+ const c2 = await db.getRepository('c').findOne();
69
+ expect(c1.get('id')).toBe(c2.get('id'));
70
+ expect(callback1).toHaveBeenCalledTimes(1);
71
+ expect(callback2).toHaveBeenCalledTimes(1);
72
+ });
73
+ });
@@ -0,0 +1,20 @@
1
+ import { BaseValueParser as ValueParser } from '../../value-parsers';
2
+
3
+ describe('number value parser', () => {
4
+ let parser: ValueParser;
5
+
6
+ beforeEach(() => {
7
+ parser = new ValueParser({}, {});
8
+ });
9
+
10
+ it('should be converted to an array', () => {
11
+ expect(parser.toArr('A/B', '/')).toEqual(['A', 'B']);
12
+ expect(parser.toArr('A,B')).toEqual(['A', 'B']);
13
+ expect(parser.toArr('A, B')).toEqual(['A', 'B']);
14
+ expect(parser.toArr('A, B')).toEqual(['A', 'B']);
15
+ expect(parser.toArr('A, B ')).toEqual(['A', 'B']);
16
+ expect(parser.toArr('A, B ')).toEqual(['A', 'B']);
17
+ expect(parser.toArr('A、 B')).toEqual(['A', 'B']);
18
+ expect(parser.toArr('A ,, B')).toEqual(['A', 'B']);
19
+ });
20
+ });
@@ -0,0 +1,67 @@
1
+ import moment from 'moment';
2
+ import { Database, mockDatabase } from '../..';
3
+ import { DateValueParser } from '../../value-parsers';
4
+
5
+ describe('number value parser', () => {
6
+ let parser: DateValueParser;
7
+ let db: Database;
8
+
9
+ beforeEach(() => {
10
+ db = mockDatabase();
11
+ db.collection({
12
+ name: 'tests',
13
+ fields: [
14
+ {
15
+ name: 'date',
16
+ type: 'date',
17
+ },
18
+ {
19
+ name: 'dateOnly',
20
+ type: 'date',
21
+ uiSchema: {
22
+ ['x-component-props']: {
23
+ showTime: false,
24
+ gmt: false,
25
+ },
26
+ },
27
+ },
28
+ {
29
+ name: 'dateTime',
30
+ type: 'date',
31
+ uiSchema: {
32
+ ['x-component-props']: {
33
+ showTime: true,
34
+ },
35
+ },
36
+ },
37
+ {
38
+ name: 'dateTimeGmt',
39
+ type: 'date',
40
+ uiSchema: {
41
+ ['x-component-props']: {
42
+ showTime: true,
43
+ gmt: true,
44
+ },
45
+ },
46
+ },
47
+ ],
48
+ });
49
+ parser = new DateValueParser({}, {});
50
+ });
51
+
52
+ const expectValue = (value, field = 'date') => {
53
+ const collection = db.getCollection('tests');
54
+ parser = new DateValueParser(collection.getField(field), {});
55
+ parser.setValue(value);
56
+ return expect(parser.getValue());
57
+ };
58
+
59
+ it('should be correct', () => {
60
+ expectValue(42510).toBe('2016-05-20T00:00:00.000Z');
61
+ expectValue('42510').toBe('2016-05-20T00:00:00.000Z');
62
+ expectValue('2016-05-20T00:00:00.000Z').toBe('2016-05-20T00:00:00.000Z');
63
+ expectValue('2016-05-20 04:22:22', 'dateOnly').toBe('2016-05-20T00:00:00.000Z');
64
+ expectValue('2016-05-20 01:00:00', 'dateTime').toBe(moment('2016-05-20 01:00:00').toISOString());
65
+ expectValue('2016-05-20 01:00:00', 'dateTimeGmt').toBe('2016-05-20T01:00:00.000Z');
66
+ });
67
+ });
@@ -0,0 +1,46 @@
1
+ import { NumberValueParser } from '../../value-parsers';
2
+
3
+ describe('number value parser', () => {
4
+ let parser: NumberValueParser;
5
+
6
+ beforeEach(() => {
7
+ parser = new NumberValueParser({}, {});
8
+ });
9
+
10
+ const expectValue = (value) => {
11
+ parser = new NumberValueParser({}, {});
12
+ parser.setValue(value);
13
+ return expect(parser.getValue());
14
+ };
15
+
16
+ it('should be number', () => {
17
+ expectValue(123).toBe(123);
18
+ expect(parser.errors.length === 0).toBeTruthy();
19
+ expectValue('123').toBe(123);
20
+ expect(parser.errors.length === 0).toBeTruthy();
21
+ expectValue('123%').toBe(1.23);
22
+ expect(parser.errors.length === 0).toBeTruthy();
23
+ expectValue('22.5507%').toBe(0.225507);
24
+ expect(parser.errors.length === 0).toBeTruthy();
25
+ });
26
+
27
+ it('should be null', () => {
28
+ expectValue('').toBe(null);
29
+ expect(parser.errors.length === 0).toBeTruthy();
30
+ expectValue('n/a').toBe(null);
31
+ expect(parser.errors.length === 0).toBeTruthy();
32
+ expectValue('-').toBe(null);
33
+ expect(parser.errors.length === 0).toBeTruthy();
34
+ });
35
+
36
+ it('should be errors', () => {
37
+ expectValue({}).toBe(null);
38
+ expect(parser.errors.length > 0).toBeTruthy();
39
+ expectValue('123a').toBe(null);
40
+ expect(parser.errors.length > 0).toBeTruthy();
41
+ expectValue('123a%').toBe(null);
42
+ expect(parser.errors.length > 0).toBeTruthy();
43
+ expectValue('aaa').toBe(null);
44
+ expect(parser.errors.length > 0).toBeTruthy();
45
+ });
46
+ });
@@ -0,0 +1,206 @@
1
+ import { Database, mockDatabase } from '../..';
2
+ import { ToManyValueParser } from '../../value-parsers';
3
+
4
+ describe('number value parser', () => {
5
+ let parser: ToManyValueParser;
6
+ let db: Database;
7
+
8
+ beforeEach(async () => {
9
+ db = mockDatabase();
10
+ db.collection({
11
+ name: 'posts',
12
+ fields: [
13
+ {
14
+ type: 'belongsToMany',
15
+ name: 'tags',
16
+ },
17
+ {
18
+ type: 'belongsToMany',
19
+ name: 'attachments',
20
+ interface: 'attachment',
21
+ },
22
+ ],
23
+ });
24
+ db.collection({
25
+ name: 'attachments',
26
+ fields: [],
27
+ });
28
+ db.collection({
29
+ name: 'tags',
30
+ fields: [
31
+ {
32
+ type: 'string',
33
+ name: 'name',
34
+ },
35
+ ],
36
+ });
37
+ await db.sync();
38
+ const tag = db.getRepository('tags');
39
+ await tag.create({
40
+ values: { name: 'tag1' },
41
+ });
42
+ });
43
+
44
+ afterEach(async () => {
45
+ await db.close();
46
+ });
47
+
48
+ const setValue = async (value) => {
49
+ const post = db.getCollection('posts');
50
+ parser = new ToManyValueParser(post.getField('tags'), {
51
+ column: {
52
+ dataIndex: ['tags', 'name'],
53
+ },
54
+ });
55
+ await parser.setValue(value);
56
+ };
57
+
58
+ const setAttachment = async (value) => {
59
+ const post = db.getCollection('posts');
60
+ parser = new ToManyValueParser(post.getField('attachments'), {});
61
+ await parser.setValue(value);
62
+ };
63
+
64
+ it('should be [1]', async () => {
65
+ await setValue('tag1');
66
+ expect(parser.errors.length).toBe(0);
67
+ expect(parser.getValue()).toEqual([1]);
68
+ });
69
+
70
+ it('should be null', async () => {
71
+ await setValue('tag2');
72
+ expect(parser.errors.length).toBe(1);
73
+ expect(parser.getValue()).toBeNull();
74
+ });
75
+
76
+ it('should be attachment', async () => {
77
+ await setAttachment('https://www.nocobase.com/images/logo.png');
78
+ expect(parser.errors.length).toBe(0);
79
+ expect(parser.getValue()).toMatchObject([
80
+ {
81
+ title: 'logo.png',
82
+ extname: '.png',
83
+ filename: 'logo.png',
84
+ url: 'https://www.nocobase.com/images/logo.png',
85
+ },
86
+ ]);
87
+ });
88
+ });
89
+
90
+ describe.only('china region', () => {
91
+ let parser: ToManyValueParser;
92
+ let db: Database;
93
+
94
+ beforeEach(async () => {
95
+ db = mockDatabase();
96
+ db.collection({
97
+ name: 'users',
98
+ fields: [
99
+ {
100
+ type: 'belongsToMany',
101
+ name: 'chinaRegion',
102
+ target: 'chinaRegions',
103
+ interface: 'chinaRegion',
104
+ targetKey: 'code',
105
+ sortBy: 'level',
106
+ },
107
+ ],
108
+ });
109
+ db.collection({
110
+ name: 'chinaRegions',
111
+ autoGenId: false,
112
+ fields: [
113
+ {
114
+ name: 'code',
115
+ type: 'string',
116
+ // unique: true,
117
+ primaryKey: true,
118
+ },
119
+ {
120
+ name: 'name',
121
+ type: 'string',
122
+ },
123
+ {
124
+ name: 'parent',
125
+ type: 'belongsTo',
126
+ target: 'chinaRegions',
127
+ targetKey: 'code',
128
+ foreignKey: 'parentCode',
129
+ },
130
+ {
131
+ name: 'children',
132
+ type: 'hasMany',
133
+ target: 'chinaRegions',
134
+ sourceKey: 'code',
135
+ foreignKey: 'parentCode',
136
+ },
137
+ {
138
+ name: 'level',
139
+ type: 'integer',
140
+ },
141
+ ],
142
+ });
143
+ await db.sync();
144
+ const areas = require('china-division/dist/areas.json');
145
+ const cities = require('china-division/dist/cities.json');
146
+ const provinces = require('china-division/dist/provinces.json');
147
+ const ChinaRegion = db.getModel('chinaRegions');
148
+ await ChinaRegion.bulkCreate(
149
+ provinces.map((item) => ({
150
+ code: item.code,
151
+ name: item.name,
152
+ level: 1,
153
+ })),
154
+ );
155
+ await ChinaRegion.bulkCreate(
156
+ cities.map((item) => ({
157
+ code: item.code,
158
+ name: item.name,
159
+ level: 2,
160
+ parentCode: item.provinceCode,
161
+ })),
162
+ );
163
+ await ChinaRegion.bulkCreate(
164
+ areas.map((item) => ({
165
+ code: item.code,
166
+ name: item.name,
167
+ level: 3,
168
+ parentCode: item.cityCode,
169
+ })),
170
+ );
171
+ });
172
+
173
+ afterEach(async () => {
174
+ await db.close();
175
+ });
176
+
177
+ const setValue = async (value) => {
178
+ const r = db.getCollection('users');
179
+ parser = new ToManyValueParser(r.getField('chinaRegion'), {});
180
+ await parser.setValue(value);
181
+ };
182
+
183
+ it('should be correct', async () => {
184
+ await setValue('北京市/市辖区');
185
+ expect(parser.errors.length).toBe(0);
186
+ expect(parser.getValue()).toEqual(['11', '1101']);
187
+
188
+ await setValue('北京市 / 市辖区');
189
+ expect(parser.errors.length).toBe(0);
190
+ expect(parser.getValue()).toEqual(['11', '1101']);
191
+
192
+ await setValue('天津市 / 市辖区');
193
+ expect(parser.errors.length).toBe(0);
194
+ expect(parser.getValue()).toEqual(['12', '1201']);
195
+ });
196
+
197
+ it('should be null', async () => {
198
+ await setValue('北京市2 / 市辖区');
199
+ expect(parser.errors.length).toBe(1);
200
+ expect(parser.getValue()).toBeNull();
201
+
202
+ await setValue('北京市 / 市辖区 2');
203
+ expect(parser.errors.length).toBe(1);
204
+ expect(parser.getValue()).toBeNull();
205
+ });
206
+ });
@@ -0,0 +1,60 @@
1
+ import { Database, mockDatabase } from '../..';
2
+ import { ToManyValueParser } from '../../value-parsers';
3
+
4
+ describe('number value parser', () => {
5
+ let parser: ToManyValueParser;
6
+ let db: Database;
7
+
8
+ beforeEach(async () => {
9
+ db = mockDatabase();
10
+ db.collection({
11
+ name: 'posts',
12
+ fields: [
13
+ {
14
+ type: 'belongsTo',
15
+ name: 'user',
16
+ },
17
+ ],
18
+ });
19
+ db.collection({
20
+ name: 'users',
21
+ fields: [
22
+ {
23
+ type: 'string',
24
+ name: 'name',
25
+ },
26
+ ],
27
+ });
28
+ await db.sync();
29
+ const r = db.getRepository('users');
30
+ await r.create({
31
+ values: { name: 'user1' },
32
+ });
33
+ });
34
+
35
+ afterEach(async () => {
36
+ await db.close();
37
+ });
38
+
39
+ const setValue = async (value) => {
40
+ const post = db.getCollection('posts');
41
+ parser = new ToManyValueParser(post.getField('user'), {
42
+ column: {
43
+ dataIndex: ['user', 'name'],
44
+ },
45
+ });
46
+ await parser.setValue(value);
47
+ };
48
+
49
+ it('should be correct', async () => {
50
+ await setValue('user1');
51
+ expect(parser.errors.length).toBe(0);
52
+ expect(parser.getValue()).toEqual([1]);
53
+ });
54
+
55
+ it('should be null', async () => {
56
+ await setValue('user2');
57
+ expect(parser.errors.length).toBe(1);
58
+ expect(parser.getValue()).toBeNull();
59
+ });
60
+ });
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
  import { readdir } from 'fs/promises';
3
- import { isPlainObject } from 'lodash';
3
+ import { cloneDeep, isPlainObject } from 'lodash';
4
4
  import { requireModule } from '@nocobase/utils';
5
5
 
6
6
  export type ImportFileExtension = 'js' | 'ts' | 'json';
@@ -36,6 +36,6 @@ export class ImporterReader {
36
36
  return typeof mod === 'function' ? mod() : mod;
37
37
  });
38
38
 
39
- return (await Promise.all(modules)).filter((module) => isPlainObject(module));
39
+ return (await Promise.all(modules)).filter((module) => isPlainObject(module)).map((module) => cloneDeep(module));
40
40
  }
41
41
  }