@strapi/database 4.6.0-beta.1 → 4.6.0

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.
@@ -0,0 +1,93 @@
1
+ 'use strict';
2
+
3
+ const { Database } = require('../index');
4
+
5
+ jest.mock('../connection', () =>
6
+ jest.fn(() => {
7
+ const trx = {
8
+ commit: jest.fn(),
9
+ rollback: jest.fn(),
10
+ };
11
+ return {
12
+ ...trx,
13
+ transaction: jest.fn(async () => trx),
14
+ };
15
+ })
16
+ );
17
+
18
+ jest.mock('../dialects', () => ({
19
+ getDialect: jest.fn(() => ({
20
+ configure: jest.fn(),
21
+ initialize: jest.fn(),
22
+ })),
23
+ }));
24
+
25
+ jest.mock('../migrations', () => ({
26
+ createMigrationsProvider: jest.fn(),
27
+ }));
28
+
29
+ const config = {
30
+ models: [
31
+ {
32
+ tableName: 'strapi_core_store_settings',
33
+ },
34
+ ],
35
+ connection: {
36
+ client: 'postgres',
37
+ connection: {
38
+ database: 'strapi',
39
+ user: 'strapi',
40
+ password: 'strapi',
41
+ port: 5432,
42
+ host: 'localhost',
43
+ },
44
+ },
45
+ };
46
+
47
+ describe('Database', () => {
48
+ describe('constructor', () => {
49
+ it('should throw an error if no config is provided', async () => {
50
+ expect(async () => Database.init()).rejects.toThrowError();
51
+ });
52
+
53
+ it('it should intialize if config is provided', async () => {
54
+ expect(() => Database.init(config)).toBeDefined();
55
+ });
56
+ });
57
+
58
+ describe('Transaction', () => {
59
+ it('transaction should be defined', async () => {
60
+ const db = await Database.init(config);
61
+ expect(db.transaction).toBeDefined();
62
+ });
63
+
64
+ it('should return value if transaction is complete', async () => {
65
+ const db = await Database.init(config);
66
+ const result = await db.transaction(async () => 'test');
67
+ expect(result).toBe('test');
68
+ expect(db.connection.commit).toHaveBeenCalledTimes(1);
69
+ });
70
+
71
+ it('rollback is called incase of error', async () => {
72
+ const db = await Database.init(config);
73
+ try {
74
+ await db.transaction(async () => {
75
+ throw new Error('test');
76
+ });
77
+ } catch {
78
+ /* ignore */
79
+ }
80
+ expect(db.connection.rollback).toHaveBeenCalledTimes(1);
81
+ });
82
+
83
+ it('should throw error', async () => {
84
+ const db = await Database.init(config);
85
+
86
+ expect(async () => {
87
+ await db.transaction(async () => {
88
+ throw new Error('test');
89
+ });
90
+ }).rejects.toThrowError('test');
91
+ });
92
+ });
93
+ });
@@ -16,8 +16,16 @@ class PostgresDialect extends Dialect {
16
16
  }
17
17
 
18
18
  async initialize() {
19
- this.db.connection.client.driver.types.setTypeParser(1082, 'text', (v) => v); // Don't cast DATE string to Date()
20
- this.db.connection.client.driver.types.setTypeParser(1700, 'text', parseFloat);
19
+ this.db.connection.client.driver.types.setTypeParser(
20
+ this.db.connection.client.driver.types.builtins.DATE,
21
+ 'text',
22
+ (v) => v
23
+ ); // Don't cast DATE string to Date()
24
+ this.db.connection.client.driver.types.setTypeParser(
25
+ this.db.connection.client.driver.types.builtins.NUMERIC,
26
+ 'text',
27
+ parseFloat
28
+ );
21
29
  }
22
30
 
23
31
  usesForeignKeys() {
@@ -1,85 +1,164 @@
1
1
  'use strict';
2
2
 
3
- const relationsOrderer = require('../relations-orderer');
3
+ const { relationsOrderer } = require('../relations-orderer');
4
4
 
5
- describe('relations orderer', () => {
6
- test('connect at the end', () => {
7
- const orderer = relationsOrderer(
8
- [
5
+ describe('Given I have some relations in the database', () => {
6
+ describe('When I connect a relation at the end', () => {
7
+ test('Then it is placed at the end with the correct order', () => {
8
+ const orderer = relationsOrderer(
9
+ [
10
+ { id: 2, order: 4 },
11
+ { id: 3, order: 10 },
12
+ ],
13
+ 'id',
14
+ 'order'
15
+ );
16
+
17
+ orderer.connect([{ id: 4, position: { end: true } }, { id: 5 }]);
18
+
19
+ expect(orderer.get()).toMatchObject([
9
20
  { id: 2, order: 4 },
10
21
  { id: 3, order: 10 },
11
- ],
12
- 'id',
13
- 'order'
14
- );
15
-
16
- orderer.connect([{ id: 4, position: { end: true } }, { id: 5 }]);
17
-
18
- expect(orderer.get()).toMatchObject([
19
- { id: 2, order: 4 },
20
- { id: 3, order: 10 },
21
- { id: 4, order: 10.5 },
22
- { id: 5, order: 10.5 },
23
- ]);
22
+ { id: 4, order: 10.5 },
23
+ { id: 5, order: 10.5 },
24
+ ]);
25
+ });
24
26
  });
25
27
 
26
- test('connect at the start', () => {
27
- const orderer = relationsOrderer(
28
- [
28
+ describe('When I connect a relation at the start', () => {
29
+ test('Then it is placed at the start with the correct order', () => {
30
+ const orderer = relationsOrderer(
31
+ [
32
+ { id: 2, order: 4 },
33
+ { id: 3, order: 10 },
34
+ ],
35
+ 'id',
36
+ 'order'
37
+ );
38
+
39
+ orderer.connect([{ id: 4, position: { start: true } }]);
40
+
41
+ expect(orderer.get()).toMatchObject([
42
+ { id: 4, order: 0.5 },
29
43
  { id: 2, order: 4 },
30
44
  { id: 3, order: 10 },
31
- ],
32
- 'id',
33
- 'order'
34
- );
35
-
36
- orderer.connect([{ id: 4, position: { start: true } }]);
37
-
38
- expect(orderer.get()).toMatchObject([
39
- { id: 4, order: 0.5 },
40
- { id: 2, order: 4 },
41
- { id: 3, order: 10 },
42
- ]);
45
+ ]);
46
+ });
43
47
  });
44
48
 
45
- test('connect multiple relations', () => {
46
- const orderer = relationsOrderer(
47
- [
49
+ describe('When I connect multiple relations using before', () => {
50
+ test('Then they are correctly ordered', () => {
51
+ const orderer = relationsOrderer(
52
+ [
53
+ { id: 2, order: 4 },
54
+ { id: 3, order: 10 },
55
+ ],
56
+ 'id',
57
+ 'order'
58
+ );
59
+
60
+ orderer.connect([
61
+ { id: 4, position: { before: 3 } },
62
+ { id: 5, position: { before: 4 } },
63
+ ]);
64
+
65
+ expect(orderer.get()).toMatchObject([
48
66
  { id: 2, order: 4 },
67
+ { id: 5, order: 9.5 },
68
+ { id: 4, order: 9.5 },
49
69
  { id: 3, order: 10 },
50
- ],
51
- 'id',
52
- 'order'
53
- );
54
-
55
- orderer.connect([
56
- { id: 4, position: { before: 2 } },
57
- { id: 4, position: { before: 3 } },
58
- { id: 5, position: { before: 4 } },
59
- ]);
60
-
61
- expect(orderer.get()).toMatchObject([
62
- { id: 2, order: 4 },
63
- { id: 5, order: 9.5 },
64
- { id: 4, order: 9.5 },
65
- { id: 3, order: 10 },
66
- ]);
70
+ ]);
71
+ });
67
72
  });
68
73
 
69
- test('connect with no initial relations', () => {
70
- const orderer = relationsOrderer([], 'id', 'order');
71
-
72
- orderer.connect([
73
- { id: 1, position: { start: true } },
74
- { id: 2, position: { start: true } },
75
- { id: 3, position: { after: 1 } },
76
- { id: 1, position: { after: 2 } },
77
- ]);
78
-
79
- expect(orderer.get()).toMatchObject([
80
- { id: 2, order: 0.5 },
81
- { id: 1, order: 0.5 },
82
- { id: 3, order: 0.5 },
83
- ]);
74
+ describe('When you connect multiple disordered relations', () => {
75
+ test('Then they are correctly ordered', () => {
76
+ const orderer = relationsOrderer(
77
+ [
78
+ { id: 1, order: 1 },
79
+ { id: 2, order: 2 },
80
+ { id: 3, order: 3 },
81
+ ],
82
+ 'id',
83
+ 'order'
84
+ );
85
+
86
+ orderer.connect([
87
+ { id: 5, position: { before: 1 } },
88
+ { id: 1, position: { before: 2 } },
89
+ { id: 2, position: { end: true } },
90
+ ]);
91
+
92
+ expect(orderer.get()).toMatchObject([
93
+ { id: 5, order: 0.5 },
94
+ { id: 1, order: 1.5 },
95
+ { id: 3, order: 3 },
96
+ { id: 2, order: 3.5 },
97
+ ]);
98
+ });
99
+ });
100
+
101
+ describe('When you connect a relation before a non-existing relation in non-strict mode', () => {
102
+ test('Then it is placed at the end', () => {
103
+ const orderer = relationsOrderer(
104
+ [
105
+ { id: 1, order: 1 },
106
+ { id: 2, order: 2 },
107
+ { id: 3, order: 3 },
108
+ ],
109
+ 'id',
110
+ 'order',
111
+ false
112
+ );
113
+
114
+ orderer.connect([{ id: 4, position: { before: 5 } }]);
115
+
116
+ expect(orderer.get()).toMatchObject([
117
+ { id: 1, order: 1 },
118
+ { id: 2, order: 2 },
119
+ { id: 3, order: 3 },
120
+ { id: 4, order: 3.5 },
121
+ ]);
122
+ });
123
+ });
124
+ });
125
+
126
+ describe('Given there are no relations in the database', () => {
127
+ describe('When you connect multiple new relations', () => {
128
+ test('Then they are correctly ordered', () => {
129
+ const orderer = relationsOrderer([], 'id', 'order');
130
+
131
+ orderer.connect([
132
+ { id: 1, position: { start: true } },
133
+ { id: 2, position: { start: true } },
134
+ { id: 3, position: { after: 1 } },
135
+ ]);
136
+
137
+ expect(orderer.get()).toMatchObject([
138
+ { id: 2, order: 0.5 },
139
+ { id: 1, order: 0.5 },
140
+ { id: 3, order: 0.5 },
141
+ ]);
142
+ });
143
+ });
144
+
145
+ describe('When you connect multiple disordered relations', () => {
146
+ test('Then they are correctly ordered', () => {
147
+ const orderer = relationsOrderer([], 'id', 'order');
148
+
149
+ orderer.connect([
150
+ { id: 5, position: { before: 1 } },
151
+ { id: 1, position: { before: 2 } },
152
+ { id: 2, position: { end: true } },
153
+ { id: 3, position: { after: 1 } },
154
+ ]);
155
+
156
+ expect(orderer.get()).toMatchObject([
157
+ { id: 5, order: 0.5 },
158
+ { id: 1, order: 0.5 },
159
+ { id: 3, order: 0.5 },
160
+ { id: 2, order: 0.5 },
161
+ ]);
162
+ });
84
163
  });
85
164
  });
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ const { sortConnectArray } = require('../relations-orderer');
4
+
5
+ describe('sortConnectArray', () => {
6
+ test('sorts connect array', () => {
7
+ const sortConnect = sortConnectArray([
8
+ { id: 5, position: { before: 1 } },
9
+ { id: 1, position: { before: 2 } },
10
+ { id: 2, position: { end: true } },
11
+ { id: 3, position: { after: 1 } },
12
+ ]);
13
+
14
+ expect(sortConnect).toMatchObject([
15
+ { id: 2, position: { end: true } },
16
+ { id: 1, position: { before: 2 } },
17
+ { id: 5, position: { before: 1 } },
18
+ { id: 3, position: { after: 1 } },
19
+ ]);
20
+ });
21
+
22
+ test('sorts connect array with initial relations', () => {
23
+ const sortConnect = sortConnectArray(
24
+ [
25
+ { id: 5, position: { before: 1 } },
26
+ { id: 1, position: { before: 2 } },
27
+ { id: 2, position: { end: true } },
28
+ { id: 3, position: { after: 1 } },
29
+ ],
30
+ [{ id: 1 }]
31
+ );
32
+
33
+ expect(sortConnect).toMatchObject([
34
+ { id: 5, position: { before: 1 } },
35
+ { id: 2, position: { end: true } },
36
+ { id: 1, position: { before: 2 } },
37
+ { id: 3, position: { after: 1 } },
38
+ ]);
39
+ });
40
+
41
+ test("error if position doesn't exist", () => {
42
+ const sortConnect = () => sortConnectArray([{ id: 1, position: { after: 2 } }]);
43
+
44
+ expect(sortConnect).toThrowError(
45
+ 'There was a problem connecting relation with id 1 at position {"after":2}. The relation with id 2 needs to be connected first.'
46
+ );
47
+ });
48
+
49
+ test('error with circular references', () => {
50
+ const sortConnect = () =>
51
+ sortConnectArray(
52
+ [
53
+ { id: 2, position: { after: 1 } },
54
+ { id: 3, position: { after: 1 } },
55
+ { id: 1, position: { after: 3 } },
56
+ ],
57
+ []
58
+ );
59
+
60
+ expect(sortConnect).toThrowError(
61
+ 'A circular reference was found in the connect array. One relation is trying to connect before/after another one that is trying to connect before/after it'
62
+ );
63
+ });
64
+
65
+ test('error when connecting same relation twice', () => {
66
+ const sortConnect = () =>
67
+ sortConnectArray(
68
+ [
69
+ { id: 1, position: { after: 2 } },
70
+ { id: 1, position: { after: 3 } },
71
+ ],
72
+ []
73
+ );
74
+
75
+ expect(sortConnect).toThrowError(
76
+ 'The relation with id 1 is already connected. You cannot connect the same relation twice.'
77
+ );
78
+ });
79
+ });
@@ -39,7 +39,7 @@ const {
39
39
  deleteRelations,
40
40
  cleanOrderColumns,
41
41
  } = require('./regular-relations');
42
- const relationsOrderer = require('./relations-orderer');
42
+ const { relationsOrderer } = require('./relations-orderer');
43
43
 
44
44
  const toId = (value) => value.id || value;
45
45
  const toIds = (value) => castArray(value || []).map(toId);
@@ -78,6 +78,9 @@ const toAssocs = (data) => {
78
78
  }
79
79
 
80
80
  return {
81
+ options: {
82
+ strict: data?.options?.strict,
83
+ },
81
84
  connect: toIdArray(data?.connect).map((elm) => ({
82
85
  id: elm.id,
83
86
  position: elm.position ? elm.position : { end: true },
@@ -224,7 +227,7 @@ const createEntityManager = (db) => {
224
227
 
225
228
  const trx = await strapi.db.transaction();
226
229
  try {
227
- await this.attachRelations(uid, id, data, { transaction: trx });
230
+ await this.attachRelations(uid, id, data, { transaction: trx.get() });
228
231
 
229
232
  await trx.commit();
230
233
  } catch (e) {
@@ -308,7 +311,7 @@ const createEntityManager = (db) => {
308
311
 
309
312
  const trx = await strapi.db.transaction();
310
313
  try {
311
- await this.updateRelations(uid, id, data, { transaction: trx });
314
+ await this.updateRelations(uid, id, data, { transaction: trx.get() });
312
315
  await trx.commit();
313
316
  } catch (e) {
314
317
  await trx.rollback();
@@ -379,7 +382,7 @@ const createEntityManager = (db) => {
379
382
 
380
383
  const trx = await strapi.db.transaction();
381
384
  try {
382
- await this.deleteRelations(uid, id, { transaction: trx });
385
+ await this.deleteRelations(uid, id, { transaction: trx.get() });
383
386
 
384
387
  await trx.commit();
385
388
  } catch (e) {
@@ -583,7 +586,12 @@ const createEntityManager = (db) => {
583
586
  });
584
587
  } else if (cleanRelationData.connect && hasOrderColumn(attribute)) {
585
588
  // use position attributes to calculate order
586
- const orderMap = relationsOrderer([], inverseJoinColumn.name, joinTable.orderColumnName)
589
+ const orderMap = relationsOrderer(
590
+ [],
591
+ inverseJoinColumn.name,
592
+ joinTable.orderColumnName,
593
+ true // Always make an strict connect when inserting
594
+ )
587
595
  .connect(relsToAdd)
588
596
  .get()
589
597
  // set the order based on the order of the ids
@@ -873,7 +881,8 @@ const createEntityManager = (db) => {
873
881
  const orderMap = relationsOrderer(
874
882
  adjacentRelations,
875
883
  inverseJoinColumn.name,
876
- joinTable.orderColumnName
884
+ joinTable.orderColumnName,
885
+ cleanRelationData.options.strict
877
886
  )
878
887
  .connect(cleanRelationData.connect)
879
888
  .getOrderMap();