velocious 1.0.435 → 1.0.436

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 (28) hide show
  1. package/build/configuration-types.js +1 -1
  2. package/build/database/query/model-class-query.js +3 -3
  3. package/build/database/record/index.js +41 -6
  4. package/build/database/record/instance-relationships/belongs-to.js +47 -19
  5. package/build/environment-handlers/node/cli/commands/generate/base-models.js +33 -12
  6. package/build/frontend-model-resource/base-resource.js +11 -14
  7. package/build/src/configuration-types.d.ts +2 -2
  8. package/build/src/configuration-types.d.ts.map +1 -1
  9. package/build/src/configuration-types.js +2 -2
  10. package/build/src/database/query/model-class-query.js +4 -4
  11. package/build/src/database/record/index.d.ts +15 -1
  12. package/build/src/database/record/index.d.ts.map +1 -1
  13. package/build/src/database/record/index.js +36 -7
  14. package/build/src/database/record/instance-relationships/belongs-to.d.ts +26 -0
  15. package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
  16. package/build/src/database/record/instance-relationships/belongs-to.js +44 -20
  17. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  18. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +33 -13
  19. package/build/src/frontend-model-resource/base-resource.d.ts +17 -0
  20. package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
  21. package/build/src/frontend-model-resource/base-resource.js +11 -12
  22. package/package.json +2 -2
  23. package/src/configuration-types.js +1 -1
  24. package/src/database/query/model-class-query.js +3 -3
  25. package/src/database/record/index.js +41 -6
  26. package/src/database/record/instance-relationships/belongs-to.js +47 -19
  27. package/src/environment-handlers/node/cli/commands/generate/base-models.js +33 -12
  28. package/src/frontend-model-resource/base-resource.js +11 -14
@@ -51,19 +51,36 @@ export default class VelociousDatabaseRecordBelongsToInstanceRelationship extend
51
51
 
52
52
  if (batched) return this.loaded()
53
53
 
54
- const foreignKey = this.getForeignKey()
55
- const foreignModelID = this.getModel().readColumn(foreignKey)
56
- const TargetModelClass = this.getTargetModelClass()
54
+ await this._loadForeignModelOrBlank()
55
+ this.setDirty(false)
56
+ this.setPreloaded(true)
57
+
58
+ return this.loaded()
59
+ }
60
+
61
+ /**
62
+ * Loads the foreign model, or marks the relationship blank for empty keys.
63
+ * @returns {Promise<void>} - Resolves after the loaded value is assigned.
64
+ */
65
+ async _loadForeignModelOrBlank() {
66
+ const TargetModelClass = this._getTargetModelClassOrFail()
67
+ const foreignModelID = this._readForeignModelID()
57
68
 
58
- if (!TargetModelClass) throw new Error("Can't load without a target model")
59
69
  if (foreignModelID === null || foreignModelID === undefined || foreignModelID === "") {
60
70
  this.setLoaded(undefined)
61
- this.setDirty(false)
62
- this.setPreloaded(true)
63
-
64
- return this.loaded()
71
+ } else {
72
+ this.setLoaded(await this._loadForeignModel({foreignModelID, TargetModelClass}))
65
73
  }
74
+ }
66
75
 
76
+ /**
77
+ * Loads the related model from the foreign key value.
78
+ * @param {object} args - Options.
79
+ * @param {string | number | null | undefined} args.foreignModelID - Foreign model ID.
80
+ * @param {TMC} args.TargetModelClass - Target model class.
81
+ * @returns {Promise<InstanceType<TMC> | undefined>} - Loaded foreign model.
82
+ */
83
+ async _loadForeignModel({foreignModelID, TargetModelClass}) {
67
84
  const primaryKey = TargetModelClass.primaryKey()
68
85
  /**
69
86
  * Where args.
@@ -72,20 +89,31 @@ export default class VelociousDatabaseRecordBelongsToInstanceRelationship extend
72
89
 
73
90
  whereArgs[primaryKey] = foreignModelID
74
91
 
75
- let query = TargetModelClass.where(whereArgs)
76
-
77
- query = this.applyScope(query)
92
+ const query = this.applyScope(TargetModelClass.where(whereArgs))
78
93
 
79
94
  const foreignModel = await query.first()
80
95
 
81
- if (foreignModel) {
82
- this.setLoaded(foreignModel)
83
- } else {
84
- this.setLoaded(undefined)
85
- }
86
- this.setDirty(false)
87
- this.setPreloaded(true)
96
+ return foreignModel || undefined
97
+ }
88
98
 
89
- return this.loaded()
99
+ /**
100
+ * Gets the required target model class.
101
+ * @returns {TMC} - Target model class.
102
+ */
103
+ _getTargetModelClassOrFail() {
104
+ const TargetModelClass = this.getTargetModelClass()
105
+
106
+ if (!TargetModelClass) throw new Error("Can't load without a target model")
107
+
108
+ return TargetModelClass
109
+ }
110
+
111
+ /**
112
+ * Reads the current foreign key value from the parent record.
113
+ * @returns {string | number | null | undefined} - Foreign model ID.
114
+ */
115
+ _readForeignModelID() {
116
+ return this.getModel().readColumn(this.getForeignKey())
90
117
  }
118
+
91
119
  }
@@ -3,8 +3,10 @@ import fileExists from "../../../../../utils/file-exists.js"
3
3
  import fs from "fs/promises"
4
4
  import * as inflection from "inflection"
5
5
 
6
- /** Maps an effective column type to the JSDoc type used in generated base models.
7
- * @type {Record<string, string>} */
6
+ /**
7
+ * Maps an effective column type to the JSDoc type used in generated base models.
8
+ * @type {Record<string, string>}
9
+ */
8
10
  const jsDocTypeByColumnType = {
9
11
  bigint: "number",
10
12
  bit: "number",
@@ -35,6 +37,23 @@ const jsDocTypeByColumnType = {
35
37
  /** Effective column types whose generated setter additionally accepts a string. */
36
38
  const setterStringInputColumnTypes = new Set(["date", "datetime", "timestamp without time zone"])
37
39
 
40
+ /**
41
+ * Generates a base-model relationship method.
42
+ * @param {{abstract?: boolean, body: string, name: string, param?: {name: string, type: string}, returns: string}} args - Method parts.
43
+ * @returns {string} - Generated method source.
44
+ */
45
+ function generatedRelationshipMethod({abstract = false, body, name, param, returns}) {
46
+ let fileContent = " /**\n"
47
+
48
+ if (abstract) fileContent += " * @abstract\n"
49
+ if (param) fileContent += ` * @param {${param.type}} ${param.name}\n`
50
+ fileContent += ` * @returns {${returns}}\n`
51
+ fileContent += " */\n"
52
+ fileContent += ` ${name}(${param ? param.name : ""}) { ${body} }\n`
53
+
54
+ return fileContent
55
+ }
56
+
38
57
  export default class DbGenerateModel extends BaseCommand {
39
58
  async execute() {
40
59
  await this.getConfiguration().initializeModels()
@@ -291,7 +310,7 @@ export default class DbGenerateModel extends BaseCommand {
291
310
  fileContent += " /**\n"
292
311
  fileContent += ` * @returns {Promise<import("${modelFilePath}").default | undefined>}\n`
293
312
  fileContent += " */\n"
294
- fileContent += ` ${relationship.getRelationshipName()}OrLoad() { return /** @type {Promise<import("${modelFilePath}").default | undefined>} */ (this.relationshipOrLoad("${relationship.getRelationshipName()}")) }\n`
313
+ fileContent += ` ${relationship.getRelationshipName()}OrLoad() { return /** @type {Promise<import("${modelFilePath}").default | undefined>} */ (this.relationshipOrLoad("${relationship.getRelationshipName()}", {preloadTranslations: true})) }\n`
295
314
 
296
315
  fileContent += "\n"
297
316
  fileContent += " /**\n"
@@ -323,17 +342,19 @@ export default class DbGenerateModel extends BaseCommand {
323
342
  fileContent += ` ${relationship.getRelationshipName()}Loaded() { return /** @type {Array<import("${recordImport}").default>} */ (this.getRelationshipByName("${relationship.getRelationshipName()}").loaded()) }\n`
324
343
 
325
344
  fileContent += "\n"
326
- fileContent += " /**\n"
327
- fileContent += " * @abstract\n"
328
- fileContent += ` * @returns {Promise<Array<import("${recordImport}").default>>}\n`
329
- fileContent += " */\n"
330
- fileContent += ` load${inflection.camelize(relationship.getRelationshipName())}() { throw new Error("Not implemented") }\n`
345
+ fileContent += generatedRelationshipMethod({
346
+ abstract: true,
347
+ body: "throw new Error(\"Not implemented\")",
348
+ name: `load${inflection.camelize(relationship.getRelationshipName())}`,
349
+ returns: `Promise<Array<import("${recordImport}").default>>`
350
+ })
331
351
 
332
352
  fileContent += "\n"
333
- fileContent += " /**\n"
334
- fileContent += ` * @returns {Promise<Array<import("${recordImport}").default>>}\n`
335
- fileContent += " */\n"
336
- fileContent += ` ${relationship.getRelationshipName()}OrLoad() { return /** @type {Promise<Array<import("${recordImport}").default>>} */ (this.relationshipOrLoad("${relationship.getRelationshipName()}")) }\n`
353
+ fileContent += generatedRelationshipMethod({
354
+ body: `return /** @type {Promise<Array<import("${recordImport}").default>>} */ (this.relationshipOrLoad("${relationship.getRelationshipName()}"))`,
355
+ name: `${relationship.getRelationshipName()}OrLoad`,
356
+ returns: `Promise<Array<import("${recordImport}").default>>`
357
+ })
337
358
 
338
359
  fileContent += "\n"
339
360
  fileContent += " /**\n"
@@ -330,18 +330,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
330
330
  const ModelClass = this.modelClass()
331
331
  const model = new ModelClass()
332
332
 
333
- await ModelClass.transaction(async () => {
334
- await this._assignWithVirtualSetters(model, filtered)
335
- await model.save()
336
-
337
- if (options.nestedAttributes) {
338
- await this._applyNestedAttributes(model, options.nestedAttributes, options.controller || null, permit)
339
- }
340
- })
341
-
342
- await this._preloadNestedWritableRelationships(model, permit)
343
-
344
- return model
333
+ return await this._saveWithNestedAttributes({filtered, model, options, permit})
345
334
  }
346
335
 
347
336
  /**
@@ -363,9 +352,17 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
363
352
  async update(model, attributes, options = {}) {
364
353
  const permit = parsePermittedParams(this.permittedParams({action: "update", ability: this.ability, locals: this.locals, params: attributes}))
365
354
  const filtered = filterWritableFrontendModelAttributes(model, attributes, this, permit.attributes)
366
- const ModelClass = this.modelClass()
367
355
 
368
- await ModelClass.transaction(async () => {
356
+ return await this._saveWithNestedAttributes({filtered, model, options, permit})
357
+ }
358
+
359
+ /**
360
+ * Saves a model and applies nested attributes in one transaction.
361
+ * @param {{filtered: Record<string, ?>, model: import("../database/record/index.js").default, options: {controller?: ?, nestedAttributes?: Record<string, ?> | null}, permit: {attributes: string[], nested: Record<string, ?>}}} args - Save arguments.
362
+ * @returns {Promise<import("../database/record/index.js").default>} - Saved model.
363
+ */
364
+ async _saveWithNestedAttributes({filtered, model, options, permit}) {
365
+ await this.modelClass().transaction(async () => {
369
366
  await this._assignWithVirtualSetters(model, filtered)
370
367
  await model.save()
371
368