velocious 1.0.435 → 1.0.437
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/build/configuration-types.js +1 -1
- package/build/database/query/model-class-query.js +7 -4
- package/build/database/record/index.js +51 -6
- package/build/database/record/instance-relationships/belongs-to.js +47 -19
- package/build/environment-handlers/node/cli/commands/generate/base-models.js +33 -12
- package/build/frontend-model-resource/base-resource.js +11 -14
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +8 -5
- package/build/src/database/record/index.d.ts +22 -1
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +45 -7
- package/build/src/database/record/instance-relationships/belongs-to.d.ts +26 -0
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +44 -20
- package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +33 -13
- package/build/src/frontend-model-resource/base-resource.d.ts +17 -0
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +11 -12
- package/package.json +2 -2
- package/src/configuration-types.js +1 -1
- package/src/database/query/model-class-query.js +7 -4
- package/src/database/record/index.js +51 -6
- package/src/database/record/instance-relationships/belongs-to.js +47 -19
- package/src/environment-handlers/node/cli/commands/generate/base-models.js +33 -12
- 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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
62
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
} else {
|
|
84
|
-
this.setLoaded(undefined)
|
|
85
|
-
}
|
|
86
|
-
this.setDirty(false)
|
|
87
|
-
this.setPreloaded(true)
|
|
96
|
+
return foreignModel || undefined
|
|
97
|
+
}
|
|
88
98
|
|
|
89
|
-
|
|
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
|
-
/**
|
|
7
|
-
*
|
|
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 +=
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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 +=
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
|
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
|
|
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
|
|