@stacksjs/actions 0.61.22 → 0.62.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.
- package/dist/upgrade.js +1 -1
- package/package.json +3 -3
- package/src/action.ts +3 -8
- package/src/helpers/utils.ts +2 -2
- package/src/orm/generate-model.ts +534 -427
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { log } from '@stacksjs/logging'
|
|
2
|
+
import { getModelName, getTableName } from '@stacksjs/orm'
|
|
2
3
|
import { path } from '@stacksjs/path'
|
|
3
4
|
import { fs, glob } from '@stacksjs/storage'
|
|
4
|
-
import { camelCase, pascalCase } from '@stacksjs/strings'
|
|
5
|
-
import type { Model, RelationConfig } from '@stacksjs/types'
|
|
6
|
-
import { isString
|
|
7
|
-
import { getModelName, getTableName} from '@stacksjs/orm'
|
|
5
|
+
import { camelCase, pascalCase, plural, singular } from '@stacksjs/strings'
|
|
6
|
+
import type { Attributes, Model, RelationConfig } from '@stacksjs/types'
|
|
7
|
+
import { isString } from '@stacksjs/validation'
|
|
8
8
|
export interface FieldArrayElement {
|
|
9
9
|
entity: string
|
|
10
10
|
charValue?: string | null
|
|
@@ -41,7 +41,7 @@ async function generateApiRoutes(modelFiles: string[]) {
|
|
|
41
41
|
if (middlewares.length) {
|
|
42
42
|
for (let i = 0; i < middlewares.length; i++) {
|
|
43
43
|
middlewareString += `'${middlewares[i]}'`
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
if (i < middlewares.length - 1) {
|
|
46
46
|
middlewareString += ','
|
|
47
47
|
}
|
|
@@ -103,13 +103,33 @@ async function writeModelNames() {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
async function
|
|
106
|
+
async function writeModelRequest() {
|
|
107
107
|
const modelFiles = glob.sync(path.userModelsPath('*.ts'))
|
|
108
108
|
|
|
109
|
+
const requestD = Bun.file(path.frameworkPath('types/requests.d.ts'))
|
|
110
|
+
let importTypes = ``
|
|
111
|
+
let importTypesString = ``
|
|
112
|
+
let typeString = `import { Request } from '../core/router/src/request'\n\n`
|
|
113
|
+
|
|
114
|
+
typeString += `interface ValidationType {
|
|
115
|
+
rule: VineType;
|
|
116
|
+
message: { [key: string]: string };
|
|
117
|
+
}\n\n`
|
|
118
|
+
|
|
119
|
+
typeString += `interface ValidationField {
|
|
120
|
+
[key: string]: string | ValidationType;
|
|
121
|
+
validation: ValidationType;
|
|
122
|
+
}\n\n`
|
|
123
|
+
|
|
124
|
+
typeString += `interface CustomAttributes {
|
|
125
|
+
[key: string]: ValidationField
|
|
126
|
+
}\n\n`
|
|
127
|
+
|
|
109
128
|
for (let i = 0; i < modelFiles.length; i++) {
|
|
129
|
+
let fieldStringType = ``
|
|
110
130
|
let fieldString = ``
|
|
111
131
|
let fieldStringInt = ``
|
|
112
|
-
let fileString = `import { Request } from '@stacksjs/router'\nimport { validateField } from '@stacksjs/validation'\nimport
|
|
132
|
+
let fileString = `import { Request } from '@stacksjs/router'\nimport type { VineType } from '@stacksjs/types'\nimport { validateField } from '@stacksjs/validation'\nimport { customValidate } from '@stacksjs/validation'\n\n`
|
|
113
133
|
|
|
114
134
|
const modeFileElement = modelFiles[i] as string
|
|
115
135
|
|
|
@@ -117,35 +137,103 @@ async function writeModelRequests() {
|
|
|
117
137
|
const modelName = getModelName(model, modeFileElement)
|
|
118
138
|
|
|
119
139
|
const attributes = await extractFields(model, modeFileElement)
|
|
120
|
-
|
|
140
|
+
|
|
141
|
+
fieldString += ` id?: number\n`
|
|
142
|
+
fieldStringInt += `public id = 1\n`
|
|
143
|
+
fieldStringType += `'id' |`
|
|
144
|
+
let keyCounter = 0
|
|
145
|
+
let keyCounterForeign = 0
|
|
146
|
+
|
|
147
|
+
const otherModelRelations = await fetchOtherModelRelations(model, modelName)
|
|
148
|
+
|
|
121
149
|
for (const attribute of attributes) {
|
|
122
150
|
let defaultValue: any = `''`
|
|
123
|
-
const entity = attribute.fieldArray?.entity === 'enum' ? 'string' : attribute.fieldArray?.entity
|
|
151
|
+
const entity = attribute.fieldArray?.entity === 'enum' ? 'string[]' : attribute.fieldArray?.entity
|
|
124
152
|
|
|
125
|
-
if (attribute.fieldArray?.entity === 'boolean')
|
|
126
|
-
defaultValue = false
|
|
153
|
+
if (attribute.fieldArray?.entity === 'boolean') defaultValue = false
|
|
127
154
|
|
|
128
|
-
if (attribute.fieldArray?.entity === 'number')
|
|
129
|
-
defaultValue = 0
|
|
155
|
+
if (attribute.fieldArray?.entity === 'number') defaultValue = 0
|
|
130
156
|
|
|
131
157
|
fieldString += ` ${attribute.field}: ${entity}\n `
|
|
132
158
|
|
|
159
|
+
fieldStringType += `'${attribute.field}'`
|
|
160
|
+
if (keyCounter < attributes.length - 1) fieldStringType += ' |'
|
|
161
|
+
|
|
133
162
|
fieldStringInt += `public ${attribute.field} = ${defaultValue}\n`
|
|
134
|
-
|
|
163
|
+
|
|
164
|
+
keyCounter++
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const otherModel of otherModelRelations) {
|
|
168
|
+
fieldString += ` ${otherModel.foreignKey}: number\n `
|
|
169
|
+
|
|
170
|
+
if (keyCounter >= attributes.length - 1) fieldStringType += ' |'
|
|
171
|
+
|
|
172
|
+
fieldStringType += `'${otherModel.foreignKey}'`
|
|
173
|
+
|
|
174
|
+
// if (keyCounterForeign < otherModelRelations.length - 1)
|
|
175
|
+
// fieldStringType += ' |'
|
|
176
|
+
|
|
177
|
+
fieldStringInt += `public ${otherModel.foreignKey} = 0\n`
|
|
178
|
+
|
|
179
|
+
keyCounterForeign++
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fieldStringInt += `public created_at = ''
|
|
183
|
+
public updated_at = ''
|
|
184
|
+
public deleted_at = ''
|
|
185
|
+
`
|
|
186
|
+
|
|
187
|
+
fieldString += `created_at?: string
|
|
188
|
+
updated_at?: string
|
|
189
|
+
deleted_at?: string`
|
|
135
190
|
|
|
136
191
|
const modelLowerCase = camelCase(modelName)
|
|
137
192
|
|
|
138
193
|
const requestFile = Bun.file(path.projectStoragePath(`framework/requests/${modelName}Request.ts`))
|
|
139
194
|
|
|
140
|
-
|
|
141
|
-
|
|
195
|
+
importTypes = `${modelName}RequestType`
|
|
196
|
+
importTypesString += `${importTypes}`
|
|
197
|
+
|
|
198
|
+
if (i < modelFiles.length - 1) importTypesString += ` | `
|
|
199
|
+
|
|
200
|
+
fileString += `import type { ${importTypes} } from '../types/requests'\n\n`
|
|
201
|
+
|
|
202
|
+
fileString += `interface ValidationType {
|
|
203
|
+
rule: VineType;
|
|
204
|
+
message: { [key: string]: string };
|
|
205
|
+
}\n\n`
|
|
206
|
+
|
|
207
|
+
fileString += `interface ValidationField {
|
|
208
|
+
[key: string]: string | ValidationType;
|
|
209
|
+
validation: ValidationType;
|
|
210
|
+
}\n\n`
|
|
211
|
+
|
|
212
|
+
fileString += `interface CustomAttributes {
|
|
213
|
+
[key: string]: ValidationField
|
|
214
|
+
}`
|
|
215
|
+
|
|
216
|
+
const types = `export interface ${modelName}RequestType extends Request {
|
|
217
|
+
validate(attributes?: CustomAttributes): void
|
|
218
|
+
get(key: ${fieldStringType}): string | number | undefined;
|
|
142
219
|
${fieldString}
|
|
143
220
|
}\n\n`
|
|
144
|
-
|
|
145
|
-
|
|
221
|
+
|
|
222
|
+
typeString += `interface RequestData${modelName} {
|
|
223
|
+
${fieldString}
|
|
224
|
+
}\n`
|
|
225
|
+
|
|
226
|
+
typeString += types
|
|
227
|
+
|
|
228
|
+
fileString += `export class ${modelName}Request extends Request implements ${modelName}RequestType {
|
|
146
229
|
${fieldStringInt}
|
|
147
|
-
public validate(): void {
|
|
148
|
-
|
|
230
|
+
public async validate(attributes?: CustomAttributes): Promise<void> {
|
|
231
|
+
if (attributes === undefined || attributes === null) {
|
|
232
|
+
await validateField('${modelName}', this.all())
|
|
233
|
+
} else {
|
|
234
|
+
await customValidate(attributes, this.all())
|
|
235
|
+
}
|
|
236
|
+
|
|
149
237
|
}
|
|
150
238
|
}
|
|
151
239
|
|
|
@@ -156,6 +244,12 @@ async function writeModelRequests() {
|
|
|
156
244
|
|
|
157
245
|
writer.write(fileString)
|
|
158
246
|
}
|
|
247
|
+
|
|
248
|
+
typeString += `export type ModelRequest = ${importTypesString}`
|
|
249
|
+
|
|
250
|
+
const requestWrite = requestD.writer()
|
|
251
|
+
|
|
252
|
+
requestWrite.write(typeString)
|
|
159
253
|
}
|
|
160
254
|
|
|
161
255
|
async function writeOrmActions(apiRoute: string, modelName: String): Promise<void> {
|
|
@@ -166,8 +260,7 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
|
|
|
166
260
|
|
|
167
261
|
let handleString = ``
|
|
168
262
|
|
|
169
|
-
|
|
170
|
-
actionString += ` import type { ${modelName}RequestType } from '../../requests/${modelName}Request'\n\n`
|
|
263
|
+
actionString += ` import type { ${modelName}RequestType } from '../../types/requests'\n\n`
|
|
171
264
|
|
|
172
265
|
if (apiRoute === 'index') {
|
|
173
266
|
handleString += `async handle(request: ${modelName}RequestType) {
|
|
@@ -203,6 +296,7 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
|
|
|
203
296
|
|
|
204
297
|
if (apiRoute === 'store') {
|
|
205
298
|
handleString += `async handle(request: ${modelName}RequestType) {
|
|
299
|
+
await request.validate()
|
|
206
300
|
const model = await ${modelName}.create(request.all())
|
|
207
301
|
|
|
208
302
|
return model
|
|
@@ -213,6 +307,8 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
|
|
|
213
307
|
|
|
214
308
|
if (apiRoute === 'update') {
|
|
215
309
|
handleString += `async handle(request: ${modelName}RequestType) {
|
|
310
|
+
await request.validate()
|
|
311
|
+
|
|
216
312
|
const id = request.getParam('id')
|
|
217
313
|
|
|
218
314
|
const model = await ${modelName}.findOrFail(Number(id))
|
|
@@ -242,10 +338,10 @@ async function initiateModelGeneration(): Promise<void> {
|
|
|
242
338
|
await deleteExistingModels()
|
|
243
339
|
await deleteExistingOrmActions()
|
|
244
340
|
await deleteExistingModelNameTypes()
|
|
245
|
-
await
|
|
341
|
+
await deleteExistingModelRequest()
|
|
246
342
|
|
|
247
343
|
await writeModelNames()
|
|
248
|
-
await
|
|
344
|
+
await writeModelRequest()
|
|
249
345
|
|
|
250
346
|
const modelFiles = glob.sync(path.userModelsPath('*.ts'))
|
|
251
347
|
|
|
@@ -296,7 +392,9 @@ async function getRelations(model: Model, modelName: string): Promise<RelationCo
|
|
|
296
392
|
relationName: relationInstance.relationName || '',
|
|
297
393
|
throughModel: relationInstance.through || '',
|
|
298
394
|
throughForeignKey: relationInstance.throughForeignKey || '',
|
|
299
|
-
pivotTable:
|
|
395
|
+
pivotTable:
|
|
396
|
+
relationInstance?.pivotTable ||
|
|
397
|
+
getPivotTableName(plural(formattedModelName), plural(modelRelation.table || '')),
|
|
300
398
|
})
|
|
301
399
|
}
|
|
302
400
|
}
|
|
@@ -305,6 +403,21 @@ async function getRelations(model: Model, modelName: string): Promise<RelationCo
|
|
|
305
403
|
return relationships
|
|
306
404
|
}
|
|
307
405
|
|
|
406
|
+
function getPivotTableName(formattedModelName: string, modelRelationTable: string): string {
|
|
407
|
+
// Create an array of the model names
|
|
408
|
+
const models = [formattedModelName, modelRelationTable]
|
|
409
|
+
|
|
410
|
+
// Sort the array alphabetically
|
|
411
|
+
models.sort()
|
|
412
|
+
|
|
413
|
+
models[0] = singular(models[0] || '')
|
|
414
|
+
|
|
415
|
+
// Join the sorted array with an underscore
|
|
416
|
+
const pivotTableName = models.join('_')
|
|
417
|
+
|
|
418
|
+
return pivotTableName
|
|
419
|
+
}
|
|
420
|
+
|
|
308
421
|
function hasRelations(obj: any, key: string): boolean {
|
|
309
422
|
return key in obj
|
|
310
423
|
}
|
|
@@ -338,12 +451,15 @@ async function deleteExistingModelNameTypes() {
|
|
|
338
451
|
if (fs.existsSync(typeFile)) await Bun.$`rm ${typeFile}`
|
|
339
452
|
}
|
|
340
453
|
|
|
341
|
-
async function
|
|
454
|
+
async function deleteExistingModelRequest() {
|
|
342
455
|
const requestFiles = glob.sync(path.projectStoragePath(`framework/requests/*.ts`))
|
|
456
|
+
const requestD = path.frameworkPath('types/requests.d.ts')
|
|
343
457
|
|
|
344
458
|
for (const requestFile of requestFiles) {
|
|
345
459
|
if (fs.existsSync(requestFile)) await Bun.$`rm ${requestFile}`
|
|
346
460
|
}
|
|
461
|
+
|
|
462
|
+
if (fs.existsSync(requestD)) await Bun.$`rm ${requestD}`
|
|
347
463
|
}
|
|
348
464
|
|
|
349
465
|
async function setKyselyTypes() {
|
|
@@ -369,7 +485,7 @@ async function setKyselyTypes() {
|
|
|
369
485
|
const model = (await import(modelFile)).default as Model
|
|
370
486
|
const modelName = getModelName(model, modelFile)
|
|
371
487
|
const pivotTables = await getPivotTables(model, modelName)
|
|
372
|
-
|
|
488
|
+
|
|
373
489
|
for (const pivotTable of pivotTables) {
|
|
374
490
|
const words = pivotTable.table.split('_')
|
|
375
491
|
|
|
@@ -413,7 +529,10 @@ async function setKyselyTypes() {
|
|
|
413
529
|
|
|
414
530
|
async function extractFields(model: Model, modelFile: string): Promise<ModelElement[]> {
|
|
415
531
|
// TODO: we can improve this type
|
|
416
|
-
|
|
532
|
+
let fields: Record<string, any> | undefined = model.attributes
|
|
533
|
+
|
|
534
|
+
if (!fields) fields = {}
|
|
535
|
+
|
|
417
536
|
const fieldKeys = Object.keys(fields)
|
|
418
537
|
|
|
419
538
|
const rules: string[] = []
|
|
@@ -504,7 +623,7 @@ function getRelationCount(relation: string): string {
|
|
|
504
623
|
|
|
505
624
|
async function getPivotTables(
|
|
506
625
|
model: Model,
|
|
507
|
-
modelName: string
|
|
626
|
+
modelName: string,
|
|
508
627
|
): Promise<{ table: string; firstForeignKey?: string; secondForeignKey?: string }[]> {
|
|
509
628
|
const pivotTable = []
|
|
510
629
|
|
|
@@ -515,8 +634,7 @@ async function getPivotTables(
|
|
|
515
634
|
const modelRelation = (await import(modelRelationPath)).default as Model
|
|
516
635
|
const formattedModelName = modelName.toLowerCase()
|
|
517
636
|
|
|
518
|
-
const firstForeignKey =
|
|
519
|
-
belongsToManyRelation.firstForeignKey || `${modelName.toLowerCase()}_${model.primaryKey}`
|
|
637
|
+
const firstForeignKey = belongsToManyRelation.firstForeignKey || `${modelName.toLowerCase()}_${model.primaryKey}`
|
|
520
638
|
const secondForeignKey =
|
|
521
639
|
belongsToManyRelation.secondForeignKey || `${modelRelation.name?.toLowerCase()}_${model.primaryKey}`
|
|
522
640
|
|
|
@@ -540,7 +658,7 @@ export async function fetchOtherModelRelations(model: Model, modelName: string):
|
|
|
540
658
|
for (let i = 0; i < modelFiles.length; i++) {
|
|
541
659
|
const modelFileElement = modelFiles[i] as string
|
|
542
660
|
const modelFile = await import(modelFileElement)
|
|
543
|
-
|
|
661
|
+
|
|
544
662
|
if (modelName === modelFile.default.name) continue
|
|
545
663
|
|
|
546
664
|
const otherModelName = getModelName(modelFile, modelFileElement)
|
|
@@ -557,13 +675,43 @@ export async function fetchOtherModelRelations(model: Model, modelName: string):
|
|
|
557
675
|
return modelRelations
|
|
558
676
|
}
|
|
559
677
|
|
|
560
|
-
|
|
678
|
+
function getHiddenAttributes(attributes: Attributes | undefined): string[] {
|
|
679
|
+
if (attributes === undefined) return []
|
|
680
|
+
|
|
681
|
+
return Object.keys(attributes).filter((key) => {
|
|
682
|
+
if (attributes === undefined) return false
|
|
683
|
+
|
|
684
|
+
return attributes[key]?.hidden === true
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function getFillableAttributes(attributes: Attributes | undefined): string[] {
|
|
689
|
+
if (attributes === undefined) return []
|
|
690
|
+
|
|
691
|
+
return Object.keys(attributes).filter((key) => {
|
|
692
|
+
if (attributes === undefined) return false
|
|
693
|
+
|
|
694
|
+
return attributes[key]?.fillable === true
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
async function generateModelString(
|
|
699
|
+
tableName: string,
|
|
700
|
+
modelName: string,
|
|
701
|
+
model: Model,
|
|
702
|
+
attributes: ModelElement[],
|
|
703
|
+
): Promise<string> {
|
|
561
704
|
const formattedTableName = pascalCase(tableName) // users -> Users
|
|
562
705
|
const formattedModelName = modelName.toLowerCase() // User -> user
|
|
563
706
|
|
|
564
707
|
let fieldString = ''
|
|
708
|
+
let constructorFields = ''
|
|
709
|
+
let declareFields = ''
|
|
710
|
+
let whereStatements = ''
|
|
711
|
+
let whereFunctionStatements = ''
|
|
565
712
|
let relationMethods = ``
|
|
566
713
|
let relationImports = ``
|
|
714
|
+
let twoFactorStatements = ''
|
|
567
715
|
|
|
568
716
|
const relations = await getRelations(model, modelName)
|
|
569
717
|
|
|
@@ -577,7 +725,6 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
577
725
|
const tableRelation = relation.table || ''
|
|
578
726
|
const pivotTableRelation = relation.pivotTable
|
|
579
727
|
const formattedModelRelation = modelRelation.toLowerCase()
|
|
580
|
-
const capitalizeTableRelation = tableRelation.charAt(0).toUpperCase() + tableRelation.slice(1)
|
|
581
728
|
|
|
582
729
|
const relationType = getRelationType(relation.relationship)
|
|
583
730
|
const relationCount = getRelationCount(relation.relationship)
|
|
@@ -592,21 +739,20 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
592
739
|
|
|
593
740
|
relationMethods += `
|
|
594
741
|
async ${relationName}() {
|
|
595
|
-
if (this
|
|
742
|
+
if (this.id === undefined)
|
|
596
743
|
throw new Error('Relation Error!')
|
|
597
744
|
|
|
598
745
|
const firstModel = await db.selectFrom('${throughTableRelation}')
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
746
|
+
.where('${foreignKeyRelation}', '=', this.id)
|
|
747
|
+
.selectAll()
|
|
748
|
+
.executeTakeFirst()
|
|
602
749
|
|
|
603
750
|
if (! firstModel)
|
|
604
751
|
throw new Error('Model Relation Not Found!')
|
|
605
752
|
|
|
606
|
-
const finalModel =
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
.executeTakeFirst()
|
|
753
|
+
const finalModel = ${modelRelation}
|
|
754
|
+
.where('${foreignKeyThroughRelation}', '=', firstModel.id)
|
|
755
|
+
.first()
|
|
610
756
|
|
|
611
757
|
return new ${modelRelation}.modelInstance(finalModel)
|
|
612
758
|
}\n\n`
|
|
@@ -617,11 +763,11 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
617
763
|
|
|
618
764
|
relationMethods += `
|
|
619
765
|
async ${relationName}() {
|
|
620
|
-
if (this
|
|
766
|
+
if (this.id === undefined)
|
|
621
767
|
throw new Error('Relation Error!')
|
|
622
768
|
|
|
623
769
|
const results = await db.selectFrom('${tableRelation}')
|
|
624
|
-
.where('${foreignKeyRelation}', '=', this
|
|
770
|
+
.where('${foreignKeyRelation}', '=', this.id)
|
|
625
771
|
.selectAll()
|
|
626
772
|
.execute()
|
|
627
773
|
|
|
@@ -633,18 +779,16 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
633
779
|
const relationName = relation.relationName || formattedModelRelation
|
|
634
780
|
relationMethods += `
|
|
635
781
|
async ${relationName}() {
|
|
636
|
-
if (this
|
|
782
|
+
if (this.id === undefined)
|
|
637
783
|
throw new Error('Relation Error!')
|
|
638
784
|
|
|
639
|
-
const model =
|
|
640
|
-
.where('${foreignKeyRelation}', '=', this
|
|
641
|
-
.selectAll()
|
|
642
|
-
.executeTakeFirst()
|
|
785
|
+
const model = ${modelRelation}
|
|
786
|
+
.where('${foreignKeyRelation}', '=', this.id).first()
|
|
643
787
|
|
|
644
788
|
if (! model)
|
|
645
789
|
throw new Error('Model Relation Not Found!')
|
|
646
790
|
|
|
647
|
-
return
|
|
791
|
+
return model
|
|
648
792
|
}\n\n`
|
|
649
793
|
}
|
|
650
794
|
|
|
@@ -656,47 +800,116 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
656
800
|
if (this.${foreignKeyRelation} === undefined)
|
|
657
801
|
throw new Error('Relation Error!')
|
|
658
802
|
|
|
659
|
-
const model = await
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
.executeTakeFirst()
|
|
803
|
+
const model = await ${modelRelation}
|
|
804
|
+
.where('id', '=', ${foreignKeyRelation})
|
|
805
|
+
.first()
|
|
663
806
|
|
|
664
807
|
if (! model)
|
|
665
808
|
throw new Error('Model Relation Not Found!')
|
|
666
809
|
|
|
667
|
-
return
|
|
810
|
+
return model
|
|
668
811
|
}\n\n`
|
|
669
812
|
}
|
|
670
813
|
|
|
671
814
|
if (relationType === 'belongsType' && relationCount === 'many') {
|
|
672
815
|
const pivotTable = pivotTableRelation || tableRelation
|
|
673
|
-
const relationName = relation.relationName || formattedModelName +
|
|
816
|
+
const relationName = relation.relationName || formattedModelName + plural(pascalCase(modelRelation))
|
|
674
817
|
|
|
675
818
|
relationMethods += `
|
|
676
819
|
async ${relationName}() {
|
|
677
|
-
if (this
|
|
820
|
+
if (this.id === undefined)
|
|
678
821
|
throw new Error('Relation Error!')
|
|
679
822
|
|
|
680
823
|
const results = await db.selectFrom('${pivotTable}')
|
|
681
|
-
.where('${foreignKeyRelation}', '=', this
|
|
824
|
+
.where('${foreignKeyRelation}', '=', this.id)
|
|
682
825
|
.selectAll()
|
|
683
826
|
.execute()
|
|
827
|
+
|
|
828
|
+
const tableRelationIds = results.map(result => result.${singular(tableRelation)}_id)
|
|
684
829
|
|
|
685
|
-
|
|
830
|
+
const relationResults = await ${modelRelation}.whereIn('id', tableRelationIds).get()
|
|
831
|
+
|
|
832
|
+
return relationResults
|
|
686
833
|
}\n\n`
|
|
687
834
|
}
|
|
688
835
|
}
|
|
689
836
|
|
|
690
|
-
|
|
837
|
+
declareFields += `public id: number | undefined \n `
|
|
838
|
+
|
|
839
|
+
constructorFields += `this.id = ${formattedModelName}?.id\n `
|
|
840
|
+
|
|
841
|
+
const useTwoFactor = model.traits?.useAuth?.useTwoFactor
|
|
842
|
+
|
|
843
|
+
if (useTwoFactor) {
|
|
844
|
+
declareFields += `public two_factor_secret: string | undefined \n`
|
|
845
|
+
constructorFields += `this.two_factor_secret = ${formattedModelName}?.two_factor_secret\n `
|
|
846
|
+
|
|
847
|
+
twoFactorStatements += `
|
|
848
|
+
async generateTwoFactorForModel() {
|
|
849
|
+
const secret = generateTwoFactorSecret()
|
|
850
|
+
|
|
851
|
+
await this.update({ 'two_factor_secret': secret })
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
verifyTwoFactorCode(code: string): boolean {
|
|
855
|
+
if (! this.${formattedModelName}) return false
|
|
856
|
+
|
|
857
|
+
const modelTwoFactorSecret = this.${formattedModelName}.two_factor_secret
|
|
858
|
+
const isValid = verifyTwoFactorCode(code, modelTwoFactorSecret)
|
|
859
|
+
|
|
860
|
+
return isValid
|
|
861
|
+
}
|
|
862
|
+
`
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
for (const attribute of attributes) {
|
|
866
|
+
const entity = attribute.fieldArray?.entity === 'enum' ? 'string[]' : attribute.fieldArray?.entity
|
|
867
|
+
|
|
868
|
+
fieldString += ` ${attribute.field}: ${entity}\n `
|
|
869
|
+
|
|
870
|
+
declareFields += `public ${attribute.field}: ${entity} | undefined \n `
|
|
871
|
+
|
|
872
|
+
constructorFields += `this.${attribute.field} = ${formattedModelName}?.${attribute.field}\n `
|
|
873
|
+
|
|
874
|
+
whereStatements += `static where${pascalCase(attribute.field)}(value: string | number | boolean | undefined | null): ${modelName}Model {
|
|
875
|
+
const instance = new this(null)
|
|
876
|
+
|
|
877
|
+
instance.query = instance.query.where('${attribute.field}', '=', value)
|
|
878
|
+
|
|
879
|
+
return instance
|
|
880
|
+
} \n\n`
|
|
881
|
+
|
|
882
|
+
whereFunctionStatements += `export async function where${pascalCase(attribute.field)}(value: string | number | boolean | undefined | null): Promise<${modelName}Model[]> {
|
|
883
|
+
const query = db.selectFrom('${tableName}').where('${attribute.field}', '=', value)
|
|
884
|
+
|
|
885
|
+
const results = await query.execute()
|
|
886
|
+
|
|
887
|
+
return results.map(modelItem => new ${modelName}Model(modelItem))
|
|
888
|
+
} \n\n`
|
|
889
|
+
}
|
|
691
890
|
|
|
692
891
|
const otherModelRelations = await fetchOtherModelRelations(model, modelName)
|
|
693
892
|
|
|
694
|
-
for (const otherModelRelation of otherModelRelations)
|
|
893
|
+
for (const otherModelRelation of otherModelRelations) {
|
|
894
|
+
fieldString += ` ${otherModelRelation.foreignKey}: number \n`
|
|
895
|
+
|
|
896
|
+
declareFields += `public ${otherModelRelation.foreignKey}: number | undefined \n `
|
|
897
|
+
|
|
898
|
+
constructorFields += `this.${otherModelRelation.foreignKey} = ${formattedModelName}?.${otherModelRelation.foreignKey}\n `
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (useTwoFactor) {
|
|
902
|
+
fieldString += `two_factor_secret: string \n`
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const hidden = JSON.stringify(getHiddenAttributes(model.attributes))
|
|
906
|
+
const fillable = JSON.stringify(getFillableAttributes(model.attributes))
|
|
695
907
|
|
|
696
908
|
return `import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely'
|
|
697
|
-
import type { Result } from '@stacksjs/error-handling'
|
|
698
|
-
import { err, handleError, ok } from '@stacksjs/error-handling'
|
|
699
909
|
import { db } from '@stacksjs/database'
|
|
910
|
+
import { sql } from '@stacksjs/database'
|
|
911
|
+
import { generateTwoFactorSecret } from '@stacksjs/auth'
|
|
912
|
+
import { verifyTwoFactorCode } from '@stacksjs/auth'
|
|
700
913
|
${relationImports}
|
|
701
914
|
// import { Kysely, MysqlDialect, PostgresDialect } from 'kysely'
|
|
702
915
|
// import { Pool } from 'pg'
|
|
@@ -737,18 +950,24 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
737
950
|
offset?: number
|
|
738
951
|
page?: number
|
|
739
952
|
}
|
|
740
|
-
|
|
953
|
+
|
|
741
954
|
export class ${modelName}Model {
|
|
742
|
-
private ${formattedModelName}: Partial<${modelName}Type>
|
|
743
|
-
private
|
|
744
|
-
private
|
|
745
|
-
|
|
746
|
-
|
|
955
|
+
private ${formattedModelName}: Partial<${modelName}Type> | null
|
|
956
|
+
private hidden = ${hidden}
|
|
957
|
+
private fillable = ${fillable}
|
|
958
|
+
protected query: any
|
|
959
|
+
protected hasSelect: boolean
|
|
960
|
+
${declareFields}
|
|
961
|
+
constructor(${formattedModelName}: Partial<${modelName}Type> | null) {
|
|
747
962
|
this.${formattedModelName} = ${formattedModelName}
|
|
963
|
+
${constructorFields}
|
|
964
|
+
|
|
965
|
+
this.query = db.selectFrom('${tableName}')
|
|
966
|
+
this.hasSelect = false
|
|
748
967
|
}
|
|
749
968
|
|
|
750
|
-
// Method to find a ${
|
|
751
|
-
|
|
969
|
+
// Method to find a ${modelName} by ID
|
|
970
|
+
async find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model | undefined> {
|
|
752
971
|
let query = db.selectFrom('${tableName}').where('id', '=', id)
|
|
753
972
|
|
|
754
973
|
if (fields)
|
|
@@ -759,30 +978,54 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
759
978
|
const model = await query.executeTakeFirst()
|
|
760
979
|
|
|
761
980
|
if (!model)
|
|
762
|
-
return
|
|
981
|
+
return undefined
|
|
982
|
+
|
|
983
|
+
return this.parseResult(this)
|
|
984
|
+
}
|
|
763
985
|
|
|
764
|
-
|
|
986
|
+
// Method to find a ${modelName} by ID
|
|
987
|
+
static async find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model | undefined> {
|
|
988
|
+
let query = db.selectFrom('${tableName}').where('id', '=', id)
|
|
989
|
+
|
|
990
|
+
const instance = new this(null)
|
|
991
|
+
|
|
992
|
+
if (fields)
|
|
993
|
+
query = query.select(fields)
|
|
994
|
+
else
|
|
995
|
+
query = query.selectAll()
|
|
996
|
+
|
|
997
|
+
const model = await query.executeTakeFirst()
|
|
998
|
+
|
|
999
|
+
if (!model)
|
|
1000
|
+
return undefined
|
|
1001
|
+
|
|
1002
|
+
return instance.parseResult(new this(model))
|
|
765
1003
|
}
|
|
766
1004
|
|
|
767
1005
|
static async findOrFail(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model> {
|
|
768
1006
|
let query = db.selectFrom('${tableName}').where('id', '=', id)
|
|
769
1007
|
|
|
1008
|
+
const instance = new this(null)
|
|
1009
|
+
|
|
770
1010
|
if (fields)
|
|
771
1011
|
query = query.select(fields)
|
|
772
1012
|
else
|
|
773
1013
|
query = query.selectAll()
|
|
774
1014
|
|
|
775
1015
|
const model = await query.executeTakeFirst()
|
|
776
|
-
|
|
1016
|
+
|
|
777
1017
|
if (!model)
|
|
778
1018
|
throw(\`No model results found for \${id}\ \`)
|
|
779
1019
|
|
|
780
|
-
|
|
1020
|
+
|
|
1021
|
+
return instance.parseResult(new this(model))
|
|
781
1022
|
}
|
|
782
1023
|
|
|
783
1024
|
static async findMany(ids: number[], fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model[]> {
|
|
784
1025
|
let query = db.selectFrom('${tableName}').where('id', 'in', ids)
|
|
785
1026
|
|
|
1027
|
+
const instance = new this(null)
|
|
1028
|
+
|
|
786
1029
|
if (fields)
|
|
787
1030
|
query = query.select(fields)
|
|
788
1031
|
else
|
|
@@ -790,11 +1033,13 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
790
1033
|
|
|
791
1034
|
const model = await query.execute()
|
|
792
1035
|
|
|
793
|
-
|
|
1036
|
+
instance.parseResult(new ${modelName}Model(modelItem))
|
|
1037
|
+
|
|
1038
|
+
return model.map(modelItem => instance.parseResult(new ${modelName}Model(modelItem)))
|
|
794
1039
|
}
|
|
795
1040
|
|
|
796
|
-
// Method to get a ${
|
|
797
|
-
static async
|
|
1041
|
+
// Method to get a ${modelName} by criteria
|
|
1042
|
+
static async fetch(criteria: Partial<${modelName}Type>, options: QueryOptions = {}): Promise<${modelName}Model[]> {
|
|
798
1043
|
let query = db.selectFrom('${tableName}')
|
|
799
1044
|
|
|
800
1045
|
// Apply sorting from options
|
|
@@ -812,8 +1057,50 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
812
1057
|
return model.map(modelItem => new ${modelName}Model(modelItem))
|
|
813
1058
|
}
|
|
814
1059
|
|
|
1060
|
+
// Method to get a ${modelName} by criteria
|
|
1061
|
+
static async get(): Promise<${modelName}Model[]> {
|
|
1062
|
+
const query = db.selectFrom('${tableName}')
|
|
1063
|
+
|
|
1064
|
+
const model = await query.selectAll().execute()
|
|
1065
|
+
|
|
1066
|
+
return model.map(modelItem => new ${modelName}Model(modelItem))
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Method to get a ${modelName} by criteria
|
|
1070
|
+
async get(): Promise<${modelName}Model[]> {
|
|
1071
|
+
if (this.hasSelect) {
|
|
1072
|
+
const model = await this.query.execute()
|
|
1073
|
+
|
|
1074
|
+
return model.map((modelItem: ${modelName}Model) => new ${modelName}Model(modelItem))
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const model = await this.query.selectAll().execute()
|
|
1078
|
+
|
|
1079
|
+
return model.map((modelItem: ${modelName}Model) => new ${modelName}Model(modelItem))
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
static async count(): Promise<number> {
|
|
1083
|
+
const instance = new this(null)
|
|
1084
|
+
|
|
1085
|
+
const results = await instance.query.selectAll().execute()
|
|
1086
|
+
|
|
1087
|
+
return results.length
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
async count(): Promise<number> {
|
|
1091
|
+
if (this.hasSelect) {
|
|
1092
|
+
const results = await this.query.execute()
|
|
1093
|
+
|
|
1094
|
+
return results.length
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
const results = await this.query.selectAll().execute()
|
|
1098
|
+
|
|
1099
|
+
return results.length
|
|
1100
|
+
}
|
|
1101
|
+
|
|
815
1102
|
// Method to get all ${tableName}
|
|
816
|
-
static async
|
|
1103
|
+
static async paginate(options: QueryOptions = { limit: 10, offset: 0, page: 1 }): Promise<${modelName}Response> {
|
|
817
1104
|
const totalRecordsResult = await db.selectFrom('${tableName}')
|
|
818
1105
|
.select(db.fn.count('id').as('total')) // Use 'id' or another actual column name
|
|
819
1106
|
.executeTakeFirst()
|
|
@@ -844,24 +1131,34 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
844
1131
|
}
|
|
845
1132
|
|
|
846
1133
|
// Method to create a new ${formattedModelName}
|
|
847
|
-
static async create(new${modelName}: New${modelName}): Promise<${modelName}Model> {
|
|
1134
|
+
static async create(new${modelName}: New${modelName}): Promise<${modelName}Model | undefined> {
|
|
1135
|
+
const instance = new this(null)
|
|
1136
|
+
const filteredValues = Object.keys(new${modelName})
|
|
1137
|
+
.filter(key => instance.fillable.includes(key))
|
|
1138
|
+
.reduce((obj: any, key) => {
|
|
1139
|
+
obj[key] = new${modelName}[key];
|
|
1140
|
+
return obj
|
|
1141
|
+
}, {})
|
|
1142
|
+
|
|
1143
|
+
if (Object.keys(filteredValues).length === 0) {
|
|
1144
|
+
return undefined
|
|
1145
|
+
}
|
|
1146
|
+
|
|
848
1147
|
const result = await db.insertInto('${tableName}')
|
|
849
|
-
.values(
|
|
1148
|
+
.values(filteredValues)
|
|
850
1149
|
.executeTakeFirstOrThrow()
|
|
851
1150
|
|
|
852
1151
|
return await find(Number(result.insertId)) as ${modelName}Model
|
|
853
1152
|
}
|
|
854
1153
|
|
|
855
|
-
// Method to remove a ${
|
|
856
|
-
static async remove(id: number): Promise
|
|
857
|
-
|
|
1154
|
+
// Method to remove a ${modelName}
|
|
1155
|
+
static async remove(id: number): Promise<void> {
|
|
1156
|
+
await db.deleteFrom('${tableName}')
|
|
858
1157
|
.where('id', '=', id)
|
|
859
|
-
.
|
|
860
|
-
|
|
861
|
-
return new ${modelName}Model(model)
|
|
1158
|
+
.execute()
|
|
862
1159
|
}
|
|
863
1160
|
|
|
864
|
-
|
|
1161
|
+
where(...args: (string | number | boolean | undefined | null)[]): ${modelName}Model {
|
|
865
1162
|
let column: any
|
|
866
1163
|
let operator: any
|
|
867
1164
|
let value: any
|
|
@@ -875,135 +1172,131 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
875
1172
|
throw new Error("Invalid number of arguments")
|
|
876
1173
|
}
|
|
877
1174
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
query = query.where(column, operator, value)
|
|
1175
|
+
this.query = this.query.where(column, operator, value)
|
|
881
1176
|
|
|
882
|
-
return
|
|
1177
|
+
return this
|
|
883
1178
|
}
|
|
884
1179
|
|
|
885
|
-
|
|
886
|
-
let
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
if (
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
criteria.name,
|
|
900
|
-
)
|
|
1180
|
+
static where(...args: (string | number | boolean | undefined | null)[]): ${modelName}Model {
|
|
1181
|
+
let column: any
|
|
1182
|
+
let operator: any
|
|
1183
|
+
let value: any
|
|
1184
|
+
|
|
1185
|
+
const instance = new this(null)
|
|
1186
|
+
|
|
1187
|
+
if (args.length === 2) {
|
|
1188
|
+
[column, value] = args
|
|
1189
|
+
operator = '='
|
|
1190
|
+
} else if (args.length === 3) {
|
|
1191
|
+
[column, operator, value] = args
|
|
1192
|
+
} else {
|
|
1193
|
+
throw new Error("Invalid number of arguments")
|
|
901
1194
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
if (criteria.created_at)
|
|
907
|
-
query = query.where('created_at', '=', criteria.created_at)
|
|
908
|
-
|
|
909
|
-
if (criteria.updated_at)
|
|
910
|
-
query = query.where('updated_at', '=', criteria.updated_at)
|
|
911
|
-
|
|
912
|
-
if (criteria.deleted_at)
|
|
913
|
-
query = query.where('deleted_at', '=', criteria.deleted_at)
|
|
914
|
-
|
|
915
|
-
// Apply sorting from options
|
|
916
|
-
if (options.sort)
|
|
917
|
-
query = query.orderBy(options.sort.column, options.sort.order)
|
|
918
|
-
|
|
919
|
-
// Apply pagination from options
|
|
920
|
-
if (options.limit !== undefined)
|
|
921
|
-
query = query.limit(options.limit)
|
|
922
|
-
|
|
923
|
-
if (options.offset !== undefined)
|
|
924
|
-
query = query.offset(options.offset)
|
|
925
|
-
|
|
926
|
-
return await query.selectAll().execute()
|
|
1195
|
+
|
|
1196
|
+
instance.query = instance.query.where(column, operator, value)
|
|
1197
|
+
|
|
1198
|
+
return instance
|
|
927
1199
|
}
|
|
928
1200
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
let query = db.selectFrom('${tableName}')
|
|
932
|
-
|
|
933
|
-
query = query.where(column, 'in', values)
|
|
934
|
-
|
|
935
|
-
// Apply sorting from options
|
|
936
|
-
if (options.sort)
|
|
937
|
-
query = query.orderBy(options.sort.column, options.sort.order)
|
|
1201
|
+
${whereStatements}
|
|
938
1202
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1203
|
+
static whereIn(column: keyof ${modelName}Type, values: any[]): ${modelName}Model {
|
|
1204
|
+
const instance = new this(null)
|
|
1205
|
+
|
|
1206
|
+
instance.query = instance.query.where(column, 'in', values)
|
|
1207
|
+
|
|
1208
|
+
return instance
|
|
1209
|
+
}
|
|
942
1210
|
|
|
943
|
-
|
|
944
|
-
|
|
1211
|
+
async first(): Promise<${modelName}Model | undefined> {
|
|
1212
|
+
const model = await this.query.selectAll().executeTakeFirst()
|
|
945
1213
|
|
|
946
|
-
|
|
1214
|
+
if (! model) {
|
|
1215
|
+
return undefined
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return new ${modelName}Model(model)
|
|
947
1219
|
}
|
|
948
1220
|
|
|
949
|
-
async
|
|
1221
|
+
async exists(): Promise<boolean> {
|
|
1222
|
+
const model = await this.query.selectAll().executeTakeFirst()
|
|
1223
|
+
|
|
1224
|
+
return model !== null || model !== undefined
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
static async first(): Promise<${modelName}Type | undefined> {
|
|
950
1228
|
return await db.selectFrom('${tableName}')
|
|
951
1229
|
.selectAll()
|
|
952
1230
|
.executeTakeFirst()
|
|
953
1231
|
}
|
|
954
1232
|
|
|
955
|
-
async last(): Promise<${modelName}Type> {
|
|
1233
|
+
async last(): Promise<${modelName}Type | undefined> {
|
|
956
1234
|
return await db.selectFrom('${tableName}')
|
|
957
1235
|
.selectAll()
|
|
958
1236
|
.orderBy('id', 'desc')
|
|
959
1237
|
.executeTakeFirst()
|
|
960
1238
|
}
|
|
961
1239
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1240
|
+
static orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc'): ${modelName}Model {
|
|
1241
|
+
const instance = new this(null)
|
|
1242
|
+
|
|
1243
|
+
instance.query = instance.orderBy(column, order)
|
|
1244
|
+
|
|
1245
|
+
return instance
|
|
967
1246
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
.execute()
|
|
1247
|
+
|
|
1248
|
+
orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc'): ${modelName}Model {
|
|
1249
|
+
this.query = this.query.orderBy(column, order)
|
|
1250
|
+
|
|
1251
|
+
return this
|
|
974
1252
|
}
|
|
975
1253
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1254
|
+
static orderByDesc(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1255
|
+
const instance = new this(null)
|
|
1256
|
+
|
|
1257
|
+
instance.query = instance.query.orderBy(column, 'desc')
|
|
1258
|
+
|
|
1259
|
+
return instance
|
|
981
1260
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1261
|
+
|
|
1262
|
+
orderByDesc(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1263
|
+
this.query = this.orderBy(column, 'desc')
|
|
1264
|
+
|
|
985
1265
|
return this
|
|
986
1266
|
}
|
|
987
1267
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1268
|
+
static orderByAsc(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1269
|
+
const instance = new this(null)
|
|
1270
|
+
|
|
1271
|
+
instance.query = instance.query.orderBy(column, 'desc')
|
|
1272
|
+
|
|
1273
|
+
return instance
|
|
991
1274
|
}
|
|
992
1275
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (this.${formattedModelName}.id === undefined)
|
|
996
|
-
return err(handleError('${modelName} ID is undefined'))
|
|
1276
|
+
orderByAsc(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1277
|
+
this.query = this.query.orderBy(column, 'desc')
|
|
997
1278
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
.where('id', '=', this.${formattedModelName}.id)
|
|
1001
|
-
.executeTakeFirst()
|
|
1279
|
+
return this
|
|
1280
|
+
}
|
|
1002
1281
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1282
|
+
// Method to update the ${tableName} instance
|
|
1283
|
+
async update(${formattedModelName}: ${modelName}Update): Promise<${modelName}Model | null> {
|
|
1284
|
+
if (this.id === undefined)
|
|
1285
|
+
throw new Error('${modelName} ID is undefined')
|
|
1005
1286
|
|
|
1006
|
-
|
|
1287
|
+
const filteredValues = Object.keys(new${modelName})
|
|
1288
|
+
.filter(key => this.fillable.includes(key))
|
|
1289
|
+
.reduce((obj, key) => {
|
|
1290
|
+
obj[key] = new${modelName}[key];
|
|
1291
|
+
return obj;
|
|
1292
|
+
}, {});
|
|
1293
|
+
|
|
1294
|
+
await db.updateTable('${tableName}')
|
|
1295
|
+
.set(filteredValues)
|
|
1296
|
+
.where('id', '=', this.id)
|
|
1297
|
+
.executeTakeFirst()
|
|
1298
|
+
|
|
1299
|
+
return await this.find(Number(this.id))
|
|
1007
1300
|
}
|
|
1008
1301
|
|
|
1009
1302
|
// Method to save (insert or update) the ${formattedModelName} instance
|
|
@@ -1025,33 +1318,47 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
1025
1318
|
|
|
1026
1319
|
// Method to delete the ${formattedModelName} instance
|
|
1027
1320
|
async delete(): Promise<void> {
|
|
1028
|
-
if (this
|
|
1321
|
+
if (this.id === undefined)
|
|
1029
1322
|
throw new Error('${modelName} ID is undefined')
|
|
1030
|
-
|
|
1323
|
+
|
|
1031
1324
|
await db.deleteFrom('${tableName}')
|
|
1032
|
-
.where('id', '=', this
|
|
1325
|
+
.where('id', '=', this.id)
|
|
1033
1326
|
.execute()
|
|
1034
|
-
|
|
1035
|
-
this.${formattedModelName} = {}
|
|
1036
1327
|
}
|
|
1037
1328
|
|
|
1038
|
-
|
|
1039
|
-
async refresh(): Promise<void> {
|
|
1040
|
-
if (this.${formattedModelName}.id === undefined)
|
|
1041
|
-
throw new Error('${modelName} ID is undefined')
|
|
1042
|
-
|
|
1043
|
-
const refreshedModel = await db.selectFrom('${tableName}')
|
|
1044
|
-
.where('id', '=', this.${formattedModelName}.id)
|
|
1045
|
-
.selectAll()
|
|
1046
|
-
.executeTakeFirst()
|
|
1047
|
-
|
|
1048
|
-
if (!refreshedModel)
|
|
1049
|
-
throw new Error('${modelName} not found')
|
|
1329
|
+
${relationMethods}
|
|
1050
1330
|
|
|
1051
|
-
|
|
1331
|
+
distinct(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1332
|
+
this.query = this.query.distinctOn(column)
|
|
1333
|
+
|
|
1334
|
+
return this
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
static distinct(column: keyof ${modelName}Type): ${modelName}Model {
|
|
1338
|
+
const instance = new this(null)
|
|
1339
|
+
|
|
1340
|
+
instance.query = instance.query.distinctOn(column)
|
|
1341
|
+
|
|
1342
|
+
return instance
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
join(table: string, firstCol: string, secondCol: string): ${modelName}Model {
|
|
1346
|
+
this.query = this.query.innerJoin(table, firstCol, secondCol)
|
|
1347
|
+
|
|
1348
|
+
return this
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
static join(table: string, firstCol: string, secondCol: string): ${modelName}Model {
|
|
1352
|
+
const instance = new this(null)
|
|
1353
|
+
|
|
1354
|
+
instance.query = instance.query.innerJoin(table, firstCol, secondCol)
|
|
1355
|
+
|
|
1356
|
+
return instance
|
|
1052
1357
|
}
|
|
1053
1358
|
|
|
1054
|
-
|
|
1359
|
+
static async rawQuery(rawQuery: string): Promise<any> {
|
|
1360
|
+
return await sql\`\${rawQuery}\`\.execute(db)
|
|
1361
|
+
}
|
|
1055
1362
|
|
|
1056
1363
|
toJSON() {
|
|
1057
1364
|
const output: Partial<${modelName}Type> = { ...this.${formattedModelName} }
|
|
@@ -1065,259 +1372,59 @@ async function generateModelString(tableName: string, modelName: string, model:
|
|
|
1065
1372
|
|
|
1066
1373
|
return output as ${modelName}
|
|
1067
1374
|
}
|
|
1068
|
-
}
|
|
1069
1375
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
if (fields)
|
|
1077
|
-
query = query.select(fields)
|
|
1078
|
-
else
|
|
1079
|
-
query = query.selectAll()
|
|
1080
|
-
|
|
1081
|
-
const model = await query.executeTakeFirst()
|
|
1376
|
+
parseResult(model: any): ${modelName}Model {
|
|
1377
|
+
for (const hiddenAttribute of this.hidden) {
|
|
1378
|
+
delete model[hiddenAttribute]
|
|
1379
|
+
delete model.${formattedModelName}[hiddenAttribute]
|
|
1380
|
+
}
|
|
1082
1381
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1382
|
+
return model
|
|
1383
|
+
}
|
|
1085
1384
|
|
|
1086
|
-
|
|
1385
|
+
${twoFactorStatements}
|
|
1087
1386
|
}
|
|
1088
1387
|
|
|
1089
|
-
|
|
1388
|
+
async function find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model | null> {
|
|
1090
1389
|
let query = db.selectFrom('${tableName}').where('id', '=', id)
|
|
1091
1390
|
|
|
1092
|
-
if (fields)
|
|
1093
|
-
|
|
1094
|
-
else
|
|
1095
|
-
query = query.selectAll()
|
|
1391
|
+
if (fields) query = query.select(fields)
|
|
1392
|
+
else query = query.selectAll()
|
|
1096
1393
|
|
|
1097
1394
|
const model = await query.executeTakeFirst()
|
|
1098
1395
|
|
|
1099
|
-
if (!model)
|
|
1100
|
-
throw(\`No model results found for \${id}\ \`)
|
|
1396
|
+
if (!model) return null
|
|
1101
1397
|
|
|
1102
1398
|
return new ${modelName}Model(model)
|
|
1103
1399
|
}
|
|
1104
1400
|
|
|
1105
|
-
export async function
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (fields)
|
|
1109
|
-
query = query.select(fields)
|
|
1110
|
-
else
|
|
1111
|
-
query = query.selectAll()
|
|
1112
|
-
|
|
1113
|
-
const model = await query.execute()
|
|
1114
|
-
|
|
1115
|
-
return model.map(modelItem => new ${modelName}Model(modelItem))
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
export async function count(): Number {
|
|
1119
|
-
const results = await db.selectFrom('${tableName}')
|
|
1120
|
-
.selectAll()
|
|
1121
|
-
.execute()
|
|
1401
|
+
export async function count(): Promise<number> {
|
|
1402
|
+
const results = await ${modelName}Model.count()
|
|
1122
1403
|
|
|
1123
|
-
return results
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
export async function get(criteria: Partial<${modelName}Type>, sort: { column: keyof ${modelName}Type, order: 'asc' | 'desc' } = { column: 'created_at', order: 'desc' }) {
|
|
1127
|
-
let query = db.selectFrom('${tableName}')
|
|
1128
|
-
|
|
1129
|
-
if (criteria.id)
|
|
1130
|
-
query = query.where('id', '=', criteria.id) // Kysely is immutable, we must re-assign
|
|
1131
|
-
|
|
1132
|
-
if (criteria.email)
|
|
1133
|
-
query = query.where('email', '=', criteria.email)
|
|
1134
|
-
|
|
1135
|
-
if (criteria.name !== undefined) {
|
|
1136
|
-
query = query.where(
|
|
1137
|
-
'name',
|
|
1138
|
-
criteria.name === null ? 'is' : '=',
|
|
1139
|
-
criteria.name,
|
|
1140
|
-
)
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
if (criteria.password)
|
|
1144
|
-
query = query.where('password', '=', criteria.password)
|
|
1145
|
-
|
|
1146
|
-
if (criteria.created_at)
|
|
1147
|
-
query = query.where('created_at', '=', criteria.created_at)
|
|
1148
|
-
|
|
1149
|
-
if (criteria.updated_at)
|
|
1150
|
-
query = query.where('updated_at', '=', criteria.updated_at)
|
|
1151
|
-
|
|
1152
|
-
if (criteria.deleted_at)
|
|
1153
|
-
query = query.where('deleted_at', '=', criteria.deleted_at)
|
|
1154
|
-
|
|
1155
|
-
// Apply sorting based on the 'sort' parameter
|
|
1156
|
-
query = query.orderBy(sort.column, sort.order)
|
|
1157
|
-
|
|
1158
|
-
return await query.selectAll().execute()
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
export async function all(limit: number = 10, offset: number = 0): Promise<${modelName}Type[]> {
|
|
1162
|
-
return await db.selectFrom('${tableName}')
|
|
1163
|
-
.selectAll()
|
|
1164
|
-
.orderBy('created_at', 'desc')
|
|
1165
|
-
.limit(limit)
|
|
1166
|
-
.offset(offset)
|
|
1167
|
-
.execute()
|
|
1404
|
+
return results
|
|
1168
1405
|
}
|
|
1169
1406
|
|
|
1170
1407
|
export async function create(new${modelName}: New${modelName}): Promise<${modelName}Model> {
|
|
1171
1408
|
const result = await db.insertInto('${tableName}')
|
|
1172
|
-
|
|
1173
|
-
|
|
1409
|
+
.values(new${modelName})
|
|
1410
|
+
.executeTakeFirstOrThrow()
|
|
1174
1411
|
|
|
1175
|
-
return await find(Number(result.insertId))
|
|
1412
|
+
return await find(Number(result.insertId)) as ${modelName}Model
|
|
1176
1413
|
}
|
|
1177
1414
|
|
|
1178
|
-
export async function
|
|
1179
|
-
|
|
1180
|
-
.selectAll()
|
|
1181
|
-
.executeTakeFirst()
|
|
1415
|
+
export async function rawQuery(rawQuery: string): Promise<any> {
|
|
1416
|
+
return await sql\`\${rawQuery}\`\.execute(db)
|
|
1182
1417
|
}
|
|
1183
1418
|
|
|
1184
|
-
export async function
|
|
1185
|
-
|
|
1186
|
-
.selectAll()
|
|
1187
|
-
.limit(limit)
|
|
1188
|
-
.execute()
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
export async function last(limit: number): Promise<${modelName}Type> {
|
|
1192
|
-
return await db.selectFrom('${tableName}')
|
|
1193
|
-
.selectAll()
|
|
1194
|
-
.orderBy('id', 'desc')
|
|
1195
|
-
.limit(limit)
|
|
1196
|
-
.execute()
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
export async function update(id: number, ${formattedModelName}Update: ${modelName}Update) {
|
|
1200
|
-
return await db.updateTable('${tableName}')
|
|
1201
|
-
.set(${formattedModelName}Update)
|
|
1419
|
+
export async function remove(id: number): Promise<void> {
|
|
1420
|
+
await db.deleteFrom('${tableName}')
|
|
1202
1421
|
.where('id', '=', id)
|
|
1203
1422
|
.execute()
|
|
1204
1423
|
}
|
|
1424
|
+
|
|
1425
|
+
${whereFunctionStatements}
|
|
1205
1426
|
|
|
1206
|
-
|
|
1207
|
-
return await db.deleteFrom('${tableName}')
|
|
1208
|
-
.where('id', '=', id)
|
|
1209
|
-
.executeTakeFirst()
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
export async function where(...args: (string | number)[]) {
|
|
1213
|
-
let column: any
|
|
1214
|
-
let operator: any
|
|
1215
|
-
let value: any
|
|
1216
|
-
|
|
1217
|
-
if (args.length === 2) {
|
|
1218
|
-
[column, value] = args
|
|
1219
|
-
operator = '='
|
|
1220
|
-
} else if (args.length === 3) {
|
|
1221
|
-
[column, operator, value] = args
|
|
1222
|
-
} else {
|
|
1223
|
-
throw new Error("Invalid number of arguments")
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
let query = db.selectFrom('${tableName}')
|
|
1227
|
-
|
|
1228
|
-
query = query.where(column, operator, value)
|
|
1229
|
-
|
|
1230
|
-
return await query.selectAll().execute()
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
export async function whereIs(
|
|
1234
|
-
criteria: Partial<${modelName}Type>,
|
|
1235
|
-
options: QueryOptions = {},
|
|
1236
|
-
) {
|
|
1237
|
-
let query = db.selectFrom('${tableName}')
|
|
1238
|
-
|
|
1239
|
-
// Apply criteria
|
|
1240
|
-
if (criteria.id)
|
|
1241
|
-
query = query.where('id', '=', criteria.id)
|
|
1242
|
-
|
|
1243
|
-
if (criteria.email)
|
|
1244
|
-
query = query.where('email', '=', criteria.email)
|
|
1245
|
-
|
|
1246
|
-
if (criteria.name !== undefined) {
|
|
1247
|
-
query = query.where(
|
|
1248
|
-
'name',
|
|
1249
|
-
criteria.name === null ? 'is' : '=',
|
|
1250
|
-
criteria.name,
|
|
1251
|
-
)
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
if (criteria.password)
|
|
1255
|
-
query = query.where('password', '=', criteria.password)
|
|
1256
|
-
|
|
1257
|
-
if (criteria.created_at)
|
|
1258
|
-
query = query.where('created_at', '=', criteria.created_at)
|
|
1259
|
-
|
|
1260
|
-
if (criteria.updated_at)
|
|
1261
|
-
query = query.where('updated_at', '=', criteria.updated_at)
|
|
1262
|
-
|
|
1263
|
-
if (criteria.deleted_at)
|
|
1264
|
-
query = query.where('deleted_at', '=', criteria.deleted_at)
|
|
1265
|
-
|
|
1266
|
-
// Apply sorting from options
|
|
1267
|
-
if (options.sort)
|
|
1268
|
-
query = query.orderBy(options.sort.column, options.sort.order)
|
|
1269
|
-
|
|
1270
|
-
// Apply pagination from options
|
|
1271
|
-
if (options.limit !== undefined)
|
|
1272
|
-
query = query.limit(options.limit)
|
|
1273
|
-
|
|
1274
|
-
if (options.offset !== undefined)
|
|
1275
|
-
query = query.offset(options.offset)
|
|
1276
|
-
|
|
1277
|
-
return await query.selectAll().execute()
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
export async function whereIn(
|
|
1281
|
-
column: keyof ${modelName}Type,
|
|
1282
|
-
values: any[],
|
|
1283
|
-
options: QueryOptions = {},
|
|
1284
|
-
) {
|
|
1285
|
-
let query = db.selectFrom('${tableName}')
|
|
1286
|
-
|
|
1287
|
-
query = query.where(column, 'in', values)
|
|
1288
|
-
|
|
1289
|
-
// Apply sorting from options
|
|
1290
|
-
if (options.sort)
|
|
1291
|
-
query = query.orderBy(options.sort.column, options.sort.order)
|
|
1292
|
-
|
|
1293
|
-
// Apply pagination from options
|
|
1294
|
-
if (options.limit !== undefined)
|
|
1295
|
-
query = query.limit(options.limit)
|
|
1296
|
-
|
|
1297
|
-
if (options.offset !== undefined)
|
|
1298
|
-
query = query.offset(options.offset)
|
|
1299
|
-
|
|
1300
|
-
return await query.selectAll().execute()
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
export const ${modelName} = {
|
|
1304
|
-
find,
|
|
1305
|
-
findOrFail,
|
|
1306
|
-
findMany,
|
|
1307
|
-
get,
|
|
1308
|
-
count,
|
|
1309
|
-
all,
|
|
1310
|
-
create,
|
|
1311
|
-
update,
|
|
1312
|
-
remove,
|
|
1313
|
-
Model,
|
|
1314
|
-
first,
|
|
1315
|
-
last,
|
|
1316
|
-
recent,
|
|
1317
|
-
where,
|
|
1318
|
-
whereIn,
|
|
1319
|
-
model: ${modelName}Model
|
|
1320
|
-
}
|
|
1427
|
+
const ${modelName} = ${modelName}Model
|
|
1321
1428
|
|
|
1322
1429
|
export default ${modelName}
|
|
1323
1430
|
`
|