@strapi/database 4.3.2 → 4.3.5

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 (57) hide show
  1. package/jest.config.js +1 -1
  2. package/lib/__tests__/lifecycles.test.js +1 -1
  3. package/lib/connection.js +4 -4
  4. package/lib/dialects/dialect.js +8 -2
  5. package/lib/dialects/index.js +2 -2
  6. package/lib/dialects/mysql/index.js +2 -2
  7. package/lib/dialects/mysql/schema-inspector.js +12 -16
  8. package/lib/dialects/postgresql/index.js +2 -2
  9. package/lib/dialects/postgresql/schema-inspector.js +30 -25
  10. package/lib/dialects/sqlite/index.js +1 -1
  11. package/lib/dialects/sqlite/schema-inspector.js +5 -5
  12. package/lib/entity-manager.js +29 -61
  13. package/lib/entity-repository.js +1 -1
  14. package/lib/errors/database.js +12 -0
  15. package/lib/errors/index.js +15 -0
  16. package/lib/errors/invalid-date.js +14 -0
  17. package/lib/errors/invalid-datetime.js +14 -0
  18. package/lib/errors/invalid-time.js +14 -0
  19. package/lib/errors/not-null.js +15 -0
  20. package/lib/fields/biginteger.js +17 -0
  21. package/lib/fields/boolean.js +39 -0
  22. package/lib/fields/date.js +16 -0
  23. package/lib/fields/datetime.js +19 -0
  24. package/lib/fields/field.js +17 -0
  25. package/lib/{fields.d.ts → fields/index.d.ts} +0 -0
  26. package/lib/fields/index.js +49 -0
  27. package/lib/fields/json.js +16 -0
  28. package/lib/fields/number.js +23 -0
  29. package/lib/fields/shared/parsers.js +69 -0
  30. package/lib/fields/string.js +17 -0
  31. package/lib/fields/time.js +17 -0
  32. package/lib/fields/timestamp.js +19 -0
  33. package/lib/index.js +1 -1
  34. package/lib/lifecycles/index.js +2 -2
  35. package/lib/lifecycles/subscribers/models-lifecycles.js +1 -1
  36. package/lib/lifecycles/subscribers/timestamps.js +2 -2
  37. package/lib/metadata/index.js +2 -2
  38. package/lib/metadata/relations.js +12 -15
  39. package/lib/migrations/index.js +5 -5
  40. package/lib/migrations/storage.js +4 -11
  41. package/lib/query/helpers/join.js +5 -5
  42. package/lib/query/helpers/order-by.js +1 -1
  43. package/lib/query/helpers/populate.js +43 -51
  44. package/lib/query/helpers/search.js +5 -5
  45. package/lib/query/helpers/transform.js +2 -2
  46. package/lib/query/helpers/where.js +17 -17
  47. package/lib/query/query-builder.js +17 -17
  48. package/lib/schema/builder.js +16 -14
  49. package/lib/schema/diff.js +16 -16
  50. package/lib/schema/index.js +1 -2
  51. package/lib/schema/schema.js +4 -4
  52. package/lib/schema/storage.js +3 -6
  53. package/lib/types/index.js +6 -6
  54. package/lib/utils/content-types.js +3 -3
  55. package/package.json +3 -3
  56. package/lib/errors.js +0 -56
  57. package/lib/fields.js +0 -231
@@ -14,36 +14,36 @@ const statuses = {
14
14
 
15
15
  const helpers = {
16
16
  hasTable(schema, tableName) {
17
- return schema.tables.findIndex(table => table.name === tableName) !== -1;
17
+ return schema.tables.findIndex((table) => table.name === tableName) !== -1;
18
18
  },
19
19
  findTable(schema, tableName) {
20
- return schema.tables.find(table => table.name === tableName);
20
+ return schema.tables.find((table) => table.name === tableName);
21
21
  },
22
22
 
23
23
  hasColumn(table, columnName) {
24
- return table.columns.findIndex(column => column.name === columnName) !== -1;
24
+ return table.columns.findIndex((column) => column.name === columnName) !== -1;
25
25
  },
26
26
  findColumn(table, columnName) {
27
- return table.columns.find(column => column.name === columnName);
27
+ return table.columns.find((column) => column.name === columnName);
28
28
  },
29
29
 
30
30
  hasIndex(table, columnName) {
31
- return table.indexes.findIndex(column => column.name === columnName) !== -1;
31
+ return table.indexes.findIndex((column) => column.name === columnName) !== -1;
32
32
  },
33
33
  findIndex(table, columnName) {
34
- return table.indexes.find(column => column.name === columnName);
34
+ return table.indexes.find((column) => column.name === columnName);
35
35
  },
36
36
 
37
37
  hasForeignKey(table, columnName) {
38
- return table.foreignKeys.findIndex(column => column.name === columnName) !== -1;
38
+ return table.foreignKeys.findIndex((column) => column.name === columnName) !== -1;
39
39
  },
40
40
  findForeignKey(table, columnName) {
41
- return table.foreignKeys.find(column => column.name === columnName);
41
+ return table.foreignKeys.find((column) => column.name === columnName);
42
42
  },
43
43
  };
44
44
 
45
- module.exports = db => {
46
- const hasChangedStatus = diff => diff.status === statuses.CHANGED;
45
+ module.exports = (db) => {
46
+ const hasChangedStatus = (diff) => diff.status === statuses.CHANGED;
47
47
 
48
48
  /**
49
49
  * Compares two indexes info
@@ -53,7 +53,7 @@ module.exports = db => {
53
53
  const diffIndexes = (oldIndex, index) => {
54
54
  const changes = [];
55
55
 
56
- if (_.difference(oldIndex.columns, index.columns).length > 0) {
56
+ if (!_.isEqual(oldIndex.columns, index.columns)) {
57
57
  changes.push('columns');
58
58
  }
59
59
 
@@ -117,7 +117,7 @@ module.exports = db => {
117
117
 
118
118
  const diffDefault = (oldColumn, column) => {
119
119
  const oldDefaultTo = oldColumn.defaultTo;
120
- const defaultTo = column.defaultTo;
120
+ const { defaultTo } = column;
121
121
 
122
122
  if (oldDefaultTo === null || _.toLower(oldDefaultTo) === 'null') {
123
123
  return _.isNil(defaultTo) || _.toLower(defaultTo) === 'null';
@@ -197,7 +197,7 @@ module.exports = db => {
197
197
  }
198
198
  }
199
199
 
200
- const hasChanged = [addedColumns, updatedColumns, removedColumns].some(arr => arr.length > 0);
200
+ const hasChanged = [addedColumns, updatedColumns, removedColumns].some((arr) => arr.length > 0);
201
201
 
202
202
  return {
203
203
  status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
@@ -237,7 +237,7 @@ module.exports = db => {
237
237
  }
238
238
  }
239
239
 
240
- const hasChanged = [addedIndexes, updatedIndexes, removedIndexes].some(arr => arr.length > 0);
240
+ const hasChanged = [addedIndexes, updatedIndexes, removedIndexes].some((arr) => arr.length > 0);
241
241
 
242
242
  return {
243
243
  status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
@@ -290,7 +290,7 @@ module.exports = db => {
290
290
  }
291
291
 
292
292
  const hasChanged = [addedForeignKeys, updatedForeignKeys, removedForeignKeys].some(
293
- arr => arr.length > 0
293
+ (arr) => arr.length > 0
294
294
  );
295
295
 
296
296
  return {
@@ -353,7 +353,7 @@ module.exports = db => {
353
353
  }
354
354
  }
355
355
 
356
- const hasChanged = [addedTables, updatedTables, removedTables].some(arr => arr.length > 0);
356
+ const hasChanged = [addedTables, updatedTables, removedTables].some((arr) => arr.length > 0);
357
357
 
358
358
  return {
359
359
  status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
@@ -10,7 +10,7 @@ const { metadataToSchema } = require('./schema');
10
10
  /**
11
11
  * @type {import('.').default}
12
12
  */
13
- const createSchemaProvider = db => {
13
+ const createSchemaProvider = (db) => {
14
14
  const schema = metadataToSchema(db.metadata);
15
15
 
16
16
  return {
@@ -87,7 +87,6 @@ const createSchemaProvider = db => {
87
87
  }
88
88
 
89
89
  debug('Schema unchanged');
90
- return;
91
90
  },
92
91
  };
93
92
  };
@@ -17,7 +17,7 @@ const createColumn = (name, attribute) => {
17
17
  };
18
18
  };
19
19
 
20
- const createTable = meta => {
20
+ const createTable = (meta) => {
21
21
  const table = {
22
22
  name: meta.tableName,
23
23
  indexes: meta.indexes || [],
@@ -96,7 +96,7 @@ const createTable = meta => {
96
96
  return table;
97
97
  };
98
98
 
99
- const getColumnType = attribute => {
99
+ const getColumnType = (attribute) => {
100
100
  if (attribute.columnType) {
101
101
  return attribute.columnType;
102
102
  }
@@ -182,7 +182,7 @@ const getColumnType = attribute => {
182
182
  }
183
183
  };
184
184
 
185
- const metadataToSchema = metadata => {
185
+ const metadataToSchema = (metadata) => {
186
186
  const schema = {
187
187
  tables: [],
188
188
  addTable(table) {
@@ -191,7 +191,7 @@ const metadataToSchema = metadata => {
191
191
  },
192
192
  };
193
193
 
194
- metadata.forEach(metadata => {
194
+ metadata.forEach((metadata) => {
195
195
  schema.addTable(createTable(metadata));
196
196
  });
197
197
 
@@ -4,11 +4,11 @@ const crypto = require('crypto');
4
4
 
5
5
  const TABLE_NAME = 'strapi_database_schema';
6
6
 
7
- module.exports = db => {
7
+ module.exports = (db) => {
8
8
  const hasSchemaTable = () => db.getSchemaConnection().hasTable(TABLE_NAME);
9
9
 
10
10
  const createSchemaTable = () => {
11
- return db.getSchemaConnection().createTable(TABLE_NAME, t => {
11
+ return db.getSchemaConnection().createTable(TABLE_NAME, (t) => {
12
12
  t.increments('id');
13
13
  t.json('schema');
14
14
  t.datetime('time', { useTz: false });
@@ -46,10 +46,7 @@ module.exports = db => {
46
46
  },
47
47
 
48
48
  hashSchema(schema) {
49
- return crypto
50
- .createHash('md5')
51
- .update(JSON.stringify(schema))
52
- .digest('hex');
49
+ return crypto.createHash('md5').update(JSON.stringify(schema)).digest('hex');
53
50
  },
54
51
 
55
52
  async add(schema) {
@@ -25,10 +25,10 @@ const STRING_TYPES = ['string', 'text', 'uid', 'email', 'enumeration', 'richtext
25
25
  const NUMBER_TYPES = ['biginteger', 'integer', 'decimal', 'float'];
26
26
 
27
27
  module.exports = {
28
- isString: type => STRING_TYPES.includes(type),
29
- isNumber: type => NUMBER_TYPES.includes(type),
30
- isScalar: type => SCALAR_TYPES.includes(type),
31
- isComponent: type => type === 'component',
32
- isDynamicZone: type => type === 'dynamiczone',
33
- isRelation: type => type === 'relation',
28
+ isString: (type) => STRING_TYPES.includes(type),
29
+ isNumber: (type) => NUMBER_TYPES.includes(type),
30
+ isScalar: (type) => SCALAR_TYPES.includes(type),
31
+ isComponent: (type) => type === 'component',
32
+ isDynamicZone: (type) => type === 'dynamiczone',
33
+ isRelation: (type) => type === 'relation',
34
34
  };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const transformAttribute = attribute => {
3
+ const transformAttribute = (attribute) => {
4
4
  switch (attribute.type) {
5
5
  case 'media': {
6
6
  return {
@@ -17,8 +17,8 @@ const transformAttribute = attribute => {
17
17
  };
18
18
 
19
19
  // TODO: model logic outside DB
20
- const transformContentTypes = contentTypes => {
21
- return contentTypes.map(contentType => {
20
+ const transformContentTypes = (contentTypes) => {
21
+ return contentTypes.map((contentType) => {
22
22
  const model = {
23
23
  ...contentType,
24
24
  // reuse new model def
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/database",
3
- "version": "4.3.2",
3
+ "version": "4.3.5",
4
4
  "description": "Strapi's database layer",
5
5
  "homepage": "https://strapi.io",
6
6
  "bugs": {
@@ -31,7 +31,7 @@
31
31
  "test:unit": "jest --verbose"
32
32
  },
33
33
  "dependencies": {
34
- "date-fns": "2.28.0",
34
+ "date-fns": "2.29.2",
35
35
  "debug": "4.3.1",
36
36
  "fs-extra": "10.0.0",
37
37
  "knex": "1.0.7",
@@ -42,5 +42,5 @@
42
42
  "node": ">=14.19.1 <=16.x.x",
43
43
  "npm": ">=6.0.0"
44
44
  },
45
- "gitHead": "7039c0d22836c94457130515b3a55eb93d2411f8"
45
+ "gitHead": "a52b0535513ff35b4ef46c9daf7be48f2ba71737"
46
46
  }
package/lib/errors.js DELETED
@@ -1,56 +0,0 @@
1
- 'use strict';
2
-
3
- /* DatabaseError */
4
- class DatabaseError extends Error {
5
- constructor(message, details = {}) {
6
- super();
7
- this.name = 'DatabaseError';
8
- this.message = message || 'A database error occured';
9
- this.details = details;
10
- }
11
- }
12
-
13
- class NotNullConstraint extends DatabaseError {
14
- constructor({ column = '' } = {}) {
15
- super();
16
- this.name = 'NotNullConstraint';
17
- this.message = `Not null constraint violation${column ? ` on column ${column}` : ''}.`;
18
- this.details = { column };
19
- this.stack = '';
20
- }
21
- }
22
-
23
- class InvalidTimeError extends DatabaseError {
24
- constructor(message) {
25
- super();
26
- this.name = 'InvalidTimeFormat';
27
- this.message = message || 'Invalid time format, expected HH:mm:ss.SSS';
28
- this.details = {};
29
- }
30
- }
31
-
32
- class InvalidDateError extends DatabaseError {
33
- constructor(message) {
34
- super();
35
- this.name = 'InvalidTimeFormat';
36
- this.message = message || 'Invalid date format, expected YYYY-MM-DD';
37
- this.details = {};
38
- }
39
- }
40
-
41
- class InvalidDateTimeError extends DatabaseError {
42
- constructor(message) {
43
- super();
44
- this.name = 'InvalidTimeFormat';
45
- this.message = message || 'Invalid datetime format, expected a timestamp or an ISO date';
46
- this.details = {};
47
- }
48
- }
49
-
50
- module.exports = {
51
- DatabaseError,
52
- NotNullConstraint,
53
- InvalidTimeError,
54
- InvalidDateError,
55
- InvalidDateTimeError,
56
- };
package/lib/fields.js DELETED
@@ -1,231 +0,0 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash/fp');
4
- const dateFns = require('date-fns');
5
- const { InvalidTimeError, InvalidDateError, InvalidDateTimeError } = require('./errors');
6
-
7
- class Field {
8
- constructor(config) {
9
- this.config = config;
10
- }
11
-
12
- toDB(value) {
13
- return value;
14
- }
15
-
16
- fromDB(value) {
17
- return value;
18
- }
19
- }
20
-
21
- class StringField extends Field {
22
- toDB(value) {
23
- return _.toString(value);
24
- }
25
-
26
- fromDB(value) {
27
- return _.toString(value);
28
- }
29
- }
30
-
31
- class JSONField extends Field {
32
- toDB(value) {
33
- return JSON.stringify(value);
34
- }
35
-
36
- fromDB(value) {
37
- if (typeof value === 'string') return JSON.parse(value);
38
- return value;
39
- }
40
- }
41
-
42
- class BooleanField extends Field {
43
- toDB(value) {
44
- if (typeof value === 'boolean') return value;
45
-
46
- if (['true', 't', '1', 1].includes(value)) {
47
- return true;
48
- }
49
-
50
- if (['false', 'f', '0', 0].includes(value)) {
51
- return false;
52
- }
53
-
54
- return Boolean(value);
55
- }
56
-
57
- fromDB(value) {
58
- if (typeof value === 'boolean') {
59
- return value;
60
- }
61
-
62
- const strVal = _.toString(value);
63
-
64
- if (strVal === '1') {
65
- return true;
66
- } else if (strVal === '0') {
67
- return false;
68
- } else {
69
- return null;
70
- }
71
- }
72
- }
73
-
74
- class NumberField extends Field {
75
- toDB(value) {
76
- const numberValue = _.toNumber(value);
77
-
78
- if (Number.isNaN(numberValue)) {
79
- throw new Error(`Expected a valid Number, got ${value}`);
80
- }
81
-
82
- return numberValue;
83
- }
84
-
85
- fromDB(value) {
86
- return _.toNumber(value);
87
- }
88
- }
89
-
90
- class BigIntegerField extends NumberField {
91
- toDB(value) {
92
- return _.toString(value);
93
- }
94
-
95
- fromDB(value) {
96
- return _.toString(value);
97
- }
98
- }
99
-
100
- const timeRegex = new RegExp('^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$');
101
- const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
102
- const partialDateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])/g;
103
-
104
- const parseTime = value => {
105
- if (dateFns.isDate(value)) return dateFns.format(value, 'HH:mm:ss.SSS');
106
-
107
- if (typeof value !== 'string') {
108
- throw new InvalidTimeError(`Expected a string, got a ${typeof value}`);
109
- }
110
- const result = value.match(timeRegex);
111
-
112
- if (result === null) {
113
- throw new InvalidTimeError('Invalid time format, expected HH:mm:ss.SSS');
114
- }
115
-
116
- const [, hours, minutes, seconds, fraction = '.000'] = result;
117
- const fractionPart = _.padCharsEnd('0', 3, fraction.slice(1));
118
-
119
- return `${hours}:${minutes}:${seconds}.${fractionPart}`;
120
- };
121
-
122
- const parseDate = value => {
123
- const found = _.isString(value) ? value.match(partialDateRegex) || [] : [];
124
- const extractedValue = found[0];
125
-
126
- if (extractedValue && !dateRegex.test(value)) {
127
- // TODO V5: throw an error when format yyyy-MM-dd is not respected
128
- // throw new InvalidDateError(`Invalid format, expected yyyy-MM-dd`);
129
- process.emitWarning(
130
- `[deprecated] Using a date format other than YYYY-MM-DD will be removed in future versions. Date received: ${value}. Date stored: ${extractedValue}.`
131
- );
132
- }
133
-
134
- let date = dateFns.parseISO(extractedValue);
135
- if (!dateFns.isValid(date)) {
136
- throw new InvalidDateError(`Invalid date`);
137
- }
138
-
139
- return extractedValue;
140
- };
141
-
142
- const parseDateTimeOrTimestamp = value => {
143
- if (dateFns.isDate(value)) return value;
144
- try {
145
- const date = dateFns.parseISO(value);
146
- if (dateFns.isValid(date)) return date;
147
-
148
- const milliUnixDate = dateFns.parse(value, 'T', new Date());
149
- if (dateFns.isValid(milliUnixDate)) return milliUnixDate;
150
-
151
- throw new InvalidDateTimeError(`Invalid format, expected a timestamp or an ISO date`);
152
- } catch (error) {
153
- throw new InvalidDateTimeError(`Invalid format, expected a timestamp or an ISO date`);
154
- }
155
- };
156
-
157
- class DateField extends Field {
158
- toDB(value) {
159
- return parseDate(value);
160
- }
161
-
162
- fromDB(value) {
163
- return value;
164
- }
165
- }
166
- class DatetimeField extends Field {
167
- toDB(value) {
168
- return parseDateTimeOrTimestamp(value);
169
- }
170
-
171
- fromDB(value) {
172
- const cast = new Date(value);
173
- return dateFns.isValid(cast) ? cast.toISOString() : null;
174
- }
175
- }
176
-
177
- class TimeField extends Field {
178
- toDB(value) {
179
- return parseTime(value);
180
- }
181
-
182
- fromDB(value) {
183
- // make sure that's a string with valid format ?
184
- return value;
185
- }
186
- }
187
- class TimestampField extends Field {
188
- toDB(value) {
189
- return parseDateTimeOrTimestamp(value);
190
- }
191
-
192
- fromDB(value) {
193
- const cast = new Date(value);
194
- return dateFns.isValid(cast) ? dateFns.format(cast, 'T') : null;
195
- }
196
- }
197
-
198
- const typeToFieldMap = {
199
- increments: Field,
200
- password: StringField,
201
- email: StringField,
202
- string: StringField,
203
- uid: StringField,
204
- richtext: StringField,
205
- text: StringField,
206
- enumeration: StringField,
207
- json: JSONField,
208
- biginteger: BigIntegerField,
209
- integer: NumberField,
210
- float: NumberField,
211
- decimal: NumberField,
212
- date: DateField,
213
- time: TimeField,
214
- datetime: DatetimeField,
215
- timestamp: TimestampField,
216
- boolean: BooleanField,
217
- };
218
-
219
- const createField = attribute => {
220
- const { type } = attribute;
221
-
222
- if (_.has(type, typeToFieldMap)) {
223
- return new typeToFieldMap[type]({});
224
- }
225
-
226
- throw new Error(`Undefined field for type ${type}`);
227
- };
228
-
229
- module.exports = {
230
- createField,
231
- };