velocious 1.0.9 → 1.0.11

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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.9",
6
+ "version": "1.0.11",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "test": "jasmine",
@@ -0,0 +1,24 @@
1
+ import Dummy from "../../dummy/index.js"
2
+ import Task from "../../dummy/src/models/task.js"
3
+
4
+ describe("Record - create", () => {
5
+ it("creates a new simple record", async () => {
6
+ await Dummy.run(async () => {
7
+ const task = new Task({name: "Test task"})
8
+ const project = task.buildProject({nameDe: "Test projekt"})
9
+
10
+ expect(project.name()).toEqual("Test projekt")
11
+ expect(project.nameEn()).toEqual(undefined)
12
+ expect(project.nameDe()).toEqual("Test projekt")
13
+
14
+ await task.save()
15
+
16
+ const sameTask = await Task.preload({project: {translations: true}}).find(task.id())
17
+ const sameProject = sameTask.project()
18
+
19
+ expect(sameProject.name()).toEqual("Test projekt")
20
+ expect(sameProject.nameEn()).toEqual(undefined)
21
+ expect(sameProject.nameDe()).toEqual("Test projekt")
22
+ })
23
+ })
24
+ })
@@ -32,5 +32,9 @@ export default new Configuration({
32
32
  })
33
33
  },
34
34
  locale: () => "en",
35
+ localeFallbacks: {
36
+ de: ["de", "en"],
37
+ en: ["en", "de"]
38
+ },
35
39
  locales: ["de", "en"]
36
40
  })
@@ -33,5 +33,9 @@ export default new Configuration({
33
33
  })
34
34
  },
35
35
  locale: () => "en",
36
+ localeFallbacks: {
37
+ de: ["de", "en"],
38
+ en: ["en", "de"]
39
+ },
36
40
  locales: ["de", "en"]
37
41
  })
@@ -8,7 +8,7 @@ export default class VelociousConfiguration {
8
8
  return this.velociousConfiguration
9
9
  }
10
10
 
11
- constructor({database, debug, directory, initializeModels, locale, locales, ...restArgs}) {
11
+ constructor({database, debug, directory, initializeModels, locale, localeFallbacks, locales, ...restArgs}) {
12
12
  restArgsError(restArgs)
13
13
 
14
14
  if (!initializeModels) throw new Error("initializeModels wasn't given")
@@ -19,6 +19,7 @@ export default class VelociousConfiguration {
19
19
  this._initializeModels = initializeModels
20
20
  this._isInitialized = false
21
21
  this.locale = locale
22
+ this.localeFallbacks = localeFallbacks
22
23
  this.locales = locales
23
24
  this.modelClasses = {}
24
25
  }
@@ -31,7 +32,7 @@ export default class VelociousConfiguration {
31
32
  return this.databasePool
32
33
  }
33
34
 
34
- getDatabasePoolType = () => {
35
+ getDatabasePoolType() {
35
36
  const poolTypeClass = digg(this, "database", "default", "master", "poolType")
36
37
 
37
38
  if (!poolTypeClass) {
@@ -41,7 +42,7 @@ export default class VelociousConfiguration {
41
42
  return poolTypeClass
42
43
  }
43
44
 
44
- getDirectory = () => {
45
+ getDirectory() {
45
46
  if (!this._directory) {
46
47
  this._directory = process.cwd()
47
48
  }
@@ -49,6 +50,11 @@ export default class VelociousConfiguration {
49
50
  return this._directory
50
51
  }
51
52
 
53
+ getLocaleFallbacks = () => this.localeFallbacks
54
+ setLocaleFallbacks(newLocaleFallbacks) {
55
+ this.localeFallbacks = newLocaleFallbacks
56
+ }
57
+
52
58
  getLocale() {
53
59
  if (typeof this.locale == "function") {
54
60
  return this.locale()
@@ -78,6 +78,22 @@ export default class VelociousDatabaseDriversBase {
78
78
  return false
79
79
  }
80
80
 
81
+ async transaction(callback) {
82
+ await this.query("BEGIN TRANSACTION")
83
+
84
+ let result
85
+
86
+ try {
87
+ result = await callback()
88
+ await this.query("COMMIT")
89
+ } catch (error) {
90
+ this.query("ROLLBACK")
91
+ throw error
92
+ }
93
+
94
+ return result
95
+ }
96
+
81
97
  async update(...args) {
82
98
  const sql = this.updateSql(...args)
83
99
 
@@ -93,8 +93,9 @@ export default class VelociousDatabaseDriversMysql extends Base{
93
93
  return deleteInstruction.toSql()
94
94
  }
95
95
 
96
- insertSql({tableName, data}) {
97
- const insert = new Insert({driver: this, tableName, data})
96
+ insertSql(args) {
97
+ const insertArgs = Object.assign({driver: this}, args)
98
+ const insert = new Insert(insertArgs)
98
99
 
99
100
  return insert.toSql()
100
101
  }
@@ -1,29 +1,4 @@
1
1
  import InsertBase from "../../../query/insert-base.js"
2
2
 
3
3
  export default class VelociousDatabaseConnectionDriversMysqlSqlInsert extends InsertBase {
4
- toSql() {
5
- let sql = `INSERT INTO ${this.getOptions().quoteTableName(this.tableName)} (`
6
- let count = 0
7
-
8
- for (let columnName in this.data) {
9
- if (count > 0) sql += ", "
10
-
11
- sql += this.getOptions().quoteColumnName(columnName)
12
- count++
13
- }
14
-
15
- sql += ") VALUES ("
16
- count = 0
17
-
18
- for (let columnName in this.data) {
19
- if (count > 0) sql += ", "
20
-
21
- sql += this.getOptions().quote(this.data[columnName])
22
- count++
23
- }
24
-
25
- sql += ")"
26
-
27
- return sql
28
- }
29
4
  }
@@ -34,8 +34,8 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
34
34
  return createTable.toSql()
35
35
  }
36
36
 
37
- deleteSql = ({tableName, conditions}) => new Delete({conditions, driver: this, tableName}).toSql()
38
- insertSql = ({tableName, data}) => new Insert({driver: this, tableName, data}).toSql()
37
+ deleteSql = (args) => new Delete(Object.assign({driver: this}, args)).toSql()
38
+ insertSql = (args) => new Insert(Object.assign({driver: this}, args)).toSql()
39
39
 
40
40
  async getTableByName(tableName) {
41
41
  const result = await this.query(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${this.quote(tableName)} LIMIT 1`)
@@ -64,6 +64,53 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
64
64
  return tables
65
65
  }
66
66
 
67
+ async insertMultiple(...args) {
68
+ if (this.supportsMultipleInsertValues()) {
69
+ return await this.insertMultipleWithSingleInsert(...args)
70
+ } else {
71
+ return await this.insertMultipleWithTransaction(...args)
72
+ }
73
+ }
74
+
75
+ supportsMultipleInsertValues() {
76
+ if (this.versionMajor >= 4) return true
77
+ if (this.versionMajor == 3 && this.versionMinor >= 8) return true
78
+ if (this.versionMajor == 3 && this.versionMinor == 7 && this.versionPatch >= 11) return true
79
+
80
+ return false
81
+ }
82
+
83
+ async insertMultipleWithSingleInsert(tableName, columns, rows) {
84
+ const sql = new Insert({columns, driver: this, rows, tableName}).toSql()
85
+
86
+ return await this.query(sql)
87
+ }
88
+
89
+ async insertMultipleWithTransaction(tableName, columns, rows) {
90
+ const sqls = []
91
+
92
+ for (const row of rows) {
93
+ const data = []
94
+
95
+ for (const columnIndex in columns) {
96
+ const columnName = columns[columnIndex]
97
+ const value = row[columnIndex]
98
+
99
+ data[columnName] = value
100
+ }
101
+
102
+ const insertSql = this.insertSql({tableName, data})
103
+
104
+ sqls.push(insertSql)
105
+ }
106
+
107
+ await this.transaction(async () => {
108
+ for (const sql of sqls) {
109
+ await this.query(sql)
110
+ }
111
+ })
112
+ }
113
+
67
114
  async lastInsertID() {
68
115
  const result = await this.query("SELECT LAST_INSERT_ROWID() AS last_insert_id")
69
116
 
@@ -80,6 +127,18 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
80
127
 
81
128
  queryToSql = (query) => new QueryParser({query}).toSql()
82
129
 
130
+ async registerVersion() {
131
+ const versionResult = await this.query("SELECT sqlite_version() AS version")
132
+
133
+ this.version = versionResult[0].version
134
+
135
+ const versionParts = this.version.split(".")
136
+
137
+ this.versionMajor = versionParts[0]
138
+ this.versionMinor = versionParts[1]
139
+ this.versionPatch = versionParts[2]
140
+ }
141
+
83
142
  escape(value) {
84
143
  const type = typeof value
85
144
 
@@ -22,6 +22,7 @@ export default class VelociousDatabaseDriversSqliteNative extends Base {
22
22
  }
23
23
 
24
24
  this.connection = await SQLite.openDatabaseAsync(databaseName)
25
+ await this.registerVersion()
25
26
  }
26
27
 
27
28
  async disconnect() {
@@ -24,6 +24,7 @@ export default class VelociousDatabaseDriversSqliteWeb extends Base {
24
24
  const databaseContent = await this.betterLocaleStorage.get(this.localStorageName())
25
25
 
26
26
  this.connection = new SQL.Database(databaseContent)
27
+ await this.registerVersion()
27
28
  }
28
29
 
29
30
  localStorageName = () => `VelociousDatabaseDriversSqliteWeb---${digg(this.getArgs(), "name")}`
@@ -1,29 +1,4 @@
1
1
  import InsertBase from "../../../query/insert-base.js"
2
2
 
3
3
  export default class VelociousDatabaseConnectionDriversMysqlSqlInsert extends InsertBase {
4
- toSql() {
5
- let sql = `INSERT INTO ${this.getOptions().quoteTableName(this.tableName)} (`
6
- let count = 0
7
-
8
- for (let columnName in this.data) {
9
- if (count > 0) sql += ", "
10
-
11
- sql += this.getOptions().quoteColumnName(columnName)
12
- count++
13
- }
14
-
15
- sql += ") VALUES ("
16
- count = 0
17
-
18
- for (let columnName in this.data) {
19
- if (count > 0) sql += ", "
20
-
21
- sql += this.getOptions().quote(this.data[columnName])
22
- count++
23
- }
24
-
25
- sql += ")"
26
-
27
- return sql
28
- }
29
4
  }
@@ -4,6 +4,7 @@ import * as inflection from "inflection"
4
4
  import JoinPlain from "./join-plain.js"
5
5
  import OrderPlain from "./order-plain.js"
6
6
  import Preloader from "./preloader.js"
7
+ import RecordNotFoundError from "../record/record-not-found-error.js"
7
8
  import SelectPlain from "./select-plain.js"
8
9
  import WhereHash from "./where-hash.js"
9
10
  import WherePlain from "./where-plain.js"
@@ -46,6 +47,21 @@ export default class VelociousDatabaseQuery {
46
47
 
47
48
  getOptions = () => this.driver.options()
48
49
 
50
+ async find(recordId) {
51
+ const conditions = {}
52
+
53
+ conditions[this.modelClass.primaryKey()] = recordId
54
+
55
+ const query = this.clone().where(conditions)
56
+ const record = await query.first()
57
+
58
+ if (!record) {
59
+ throw new RecordNotFoundError(`Couldn't find ${this.modelClass.name} with '${this.modelClass.primaryKey()}'=${recordId}`)
60
+ }
61
+
62
+ return record
63
+ }
64
+
49
65
  async findBy(conditions) {
50
66
  const newConditions = {}
51
67
 
@@ -1,11 +1,17 @@
1
+ import restArgsError from "../../utils/rest-args-error.js"
2
+
1
3
  export default class VelociousDatabaseQueryInsertBase {
2
- constructor({driver, tableName, data}) {
4
+ constructor({columns, data, driver, multiple, tableName, rows, ...restArgs}) {
3
5
  if (!driver) throw new Error("No driver given to insert base")
4
6
  if (!tableName) throw new Error(`Invalid table name given to insert base: ${tableName}`)
5
- if (!data) throw new Error("No data given to insert base")
6
7
 
8
+ restArgsError(restArgs)
9
+
10
+ this.columns = columns
7
11
  this.data = data
8
12
  this.driver = driver
13
+ this.multiple = multiple
14
+ this.rows = rows
9
15
  this.tableName = tableName
10
16
  }
11
17
 
@@ -14,6 +20,56 @@ export default class VelociousDatabaseQueryInsertBase {
14
20
  }
15
21
 
16
22
  toSql() {
17
- throw new Error("'toSql' wasn't implemented")
23
+ let sql = `INSERT INTO ${this.getOptions().quoteTableName(this.tableName)} (`
24
+ let count = 0
25
+ let columns
26
+
27
+ if (this.columns && this.rows) {
28
+ columns = this.columns
29
+ } else if (this.data) {
30
+ columns = Object.keys(this.data)
31
+ } else {
32
+ throw new Error("Neither 'column' and 'rows' or data was given")
33
+ }
34
+
35
+ for (const columnName of columns) {
36
+ if (count > 0) sql += ", "
37
+
38
+ sql += this.getOptions().quoteColumnName(columnName)
39
+ count++
40
+ }
41
+
42
+ sql += ") VALUES "
43
+
44
+ if (this.columns && this.rows) {
45
+ let count = 0
46
+
47
+ for (const row of this.rows) {
48
+ if (count >= 1) sql += ", "
49
+
50
+ count++
51
+ sql += this._valuesSql(row)
52
+ }
53
+ } else {
54
+ sql += this._valuesSql(Object.values(this.data))
55
+ }
56
+
57
+ return sql
58
+ }
59
+
60
+ _valuesSql(data) {
61
+ let count = 0
62
+ let sql = "("
63
+
64
+ for (const value of data) {
65
+ if (count > 0) sql += ", "
66
+
67
+ sql += this.getOptions().quote(value)
68
+ count++
69
+ }
70
+
71
+ sql += ")"
72
+
73
+ return sql
18
74
  }
19
75
  }
@@ -7,7 +7,6 @@ import HasManyRelationship from "./relationships/has-many.js"
7
7
  import HasManyInstanceRelationship from "./instance-relationships/has-many.js"
8
8
  import * as inflection from "inflection"
9
9
  import Query from "../query/index.js"
10
- import RecordNotFoundError from "./record-not-found-error.js"
11
10
 
12
11
  export default class VelociousDatabaseRecord {
13
12
  static _relationshipExists(relationshipName) {
@@ -19,6 +18,7 @@ export default class VelociousDatabaseRecord {
19
18
  }
20
19
 
21
20
  static _defineRelationship(relationshipName, data) {
21
+ if (!relationshipName) throw new Error(`Invalid relationship name given: ${relationshipName}`)
22
22
  if (!this._relationships) this._relationships = {}
23
23
  if (this._relationshipExists(relationshipName)) throw new Error(`Relationship ${relationshipName} already exists`)
24
24
 
@@ -124,21 +124,6 @@ export default class VelociousDatabaseRecord {
124
124
  return record
125
125
  }
126
126
 
127
- static async find(recordId) {
128
- const conditions = {}
129
-
130
- conditions[this.primaryKey()] = recordId
131
-
132
- const query = this.where(conditions)
133
- const record = await query.first()
134
-
135
- if (!record) {
136
- throw new RecordNotFoundError(`Couldn't find ${this.name} with '${this.primaryKey()}'=${recordId}`)
137
- }
138
-
139
- return record
140
- }
141
-
142
127
  static _getConfiguration() {
143
128
  if (!this._configuration) {
144
129
  this._configuration = Configuration.current()
@@ -210,7 +195,7 @@ export default class VelociousDatabaseRecord {
210
195
  this.prototype[name] = function () {
211
196
  const locale = this._getConfiguration().getLocale()
212
197
 
213
- return this._getTranslatedAttribute(name, locale)
198
+ return this._getTranslatedAttributeWithFallback(name, locale)
214
199
  }
215
200
 
216
201
  this.prototype[setterMethodName] = function (newValue) {
@@ -275,6 +260,10 @@ export default class VelociousDatabaseRecord {
275
260
  return this._table
276
261
  }
277
262
 
263
+ static async insertMultiple(columns, rows) {
264
+ return await this.connection().insertMultiple(this.tableName(), columns, rows)
265
+ }
266
+
278
267
  static async last() {
279
268
  const query = this._newQuery().order(this.primaryKey())
280
269
  const record = await query.last()
@@ -414,10 +403,31 @@ export default class VelociousDatabaseRecord {
414
403
  }
415
404
  }
416
405
 
406
+ _getTranslatedAttributeWithFallback(name, locale) {
407
+ let localesInOrder
408
+ const fallbacks = this._getConfiguration().getLocaleFallbacks()
409
+
410
+ if (fallbacks && locale in fallbacks) {
411
+ localesInOrder = fallbacks[locale]
412
+ } else {
413
+ localesInOrder = [locale]
414
+ }
415
+
416
+ for (const fallbackLocale of localesInOrder) {
417
+ const result = this._getTranslatedAttribute(name, fallbackLocale)
418
+
419
+ if (result && result.trim() != "") {
420
+ return result
421
+ }
422
+ }
423
+ }
424
+
417
425
  _setTranslatedAttribute(name, locale, newValue) {
418
- let translation = this.translations().loaded()?.find((translation) =>translation.locale() == locale)
426
+ let translation = this.translations().loaded()?.find((translation) => translation.locale() == locale)
419
427
 
420
- if (!translation) translation = this.translations().build({locale})
428
+ if (!translation) {
429
+ translation = this.translations().build({locale})
430
+ }
421
431
 
422
432
  const assignments = {}
423
433
 
@@ -447,6 +457,10 @@ export default class VelociousDatabaseRecord {
447
457
  return this._newQuery()
448
458
  }
449
459
 
460
+ static async find(...args) {
461
+ return this._newQuery().find(...args)
462
+ }
463
+
450
464
  static async findBy(...args) {
451
465
  return this._newQuery().findBy(...args)
452
466
  }
@@ -519,7 +533,7 @@ export default class VelociousDatabaseRecord {
519
533
  _hasChanges = () => Object.keys(this._changes).length > 0
520
534
 
521
535
  isChanged() {
522
- if (this.isNewRecord() || this._hasChanges()) {
536
+ if (this.isNewRecord() || this._hasChanges()){
523
537
  return true
524
538
  }
525
539
 
@@ -543,6 +557,18 @@ export default class VelociousDatabaseRecord {
543
557
  return false
544
558
  }
545
559
 
560
+ changes() {
561
+ const changes = {}
562
+
563
+ for (const changeKey in this._changes) {
564
+ const changeValue = this._changes[changeKey]
565
+
566
+ changes[changeKey] = [this._attributes[changeKey], changeValue]
567
+ }
568
+
569
+ return changes
570
+ }
571
+
546
572
  _tableName() {
547
573
  if (this.__tableName) return this.__tableName
548
574
 
@@ -15,5 +15,9 @@ export default new Configuration({
15
15
  database: "database"
16
16
  }
17
17
  }
18
+ },
19
+ localeFallbacks: {
20
+ de: ["de", "en"],
21
+ en: ["en", "de"]
18
22
  }
19
23
  })