@postxl/generator 0.74.2 → 1.0.1
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/LICENSE +50 -0
- package/README.md +79 -1
- package/dist/generator-manager.class.d.ts +59 -0
- package/dist/generator-manager.class.js +221 -0
- package/dist/generator.class.d.ts +90 -0
- package/dist/generator.class.js +32 -0
- package/dist/generator.context.d.ts +174 -0
- package/dist/generator.context.js +125 -0
- package/dist/helpers/branded.types.d.ts +149 -0
- package/dist/helpers/branded.types.js +111 -0
- package/dist/helpers/config-builder.class.d.ts +27 -0
- package/dist/helpers/config-builder.class.js +54 -0
- package/dist/helpers/import-generator.class.d.ts +70 -0
- package/dist/helpers/import-generator.class.js +166 -0
- package/dist/helpers/importable.types.d.ts +52 -0
- package/dist/helpers/importable.types.js +15 -0
- package/dist/helpers/index-generator.class.d.ts +10 -0
- package/dist/helpers/index-generator.class.js +46 -0
- package/dist/helpers/index.d.ts +8 -0
- package/dist/helpers/index.js +24 -0
- package/dist/helpers/package-json.generator.d.ts +56 -0
- package/dist/helpers/package-json.generator.js +36 -0
- package/dist/helpers/tsconfig.generator.d.ts +1 -0
- package/dist/helpers/tsconfig.generator.js +14 -0
- package/dist/helpers/verify-context.d.ts +4 -0
- package/dist/helpers/verify-context.js +23 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/utils/checksum.d.ts +10 -0
- package/dist/utils/checksum.js +132 -0
- package/dist/utils/fs-utils.d.ts +34 -0
- package/dist/utils/fs-utils.js +126 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/jsdoc.d.ts +12 -0
- package/dist/utils/jsdoc.js +37 -0
- package/dist/utils/lint.d.ts +46 -0
- package/dist/utils/lint.js +154 -0
- package/dist/utils/lockfile.d.ts +7 -0
- package/dist/utils/lockfile.js +80 -0
- package/dist/utils/logger.class.d.ts +25 -0
- package/dist/utils/logger.class.js +55 -0
- package/dist/utils/merge-conflict.d.ts +55 -0
- package/dist/utils/merge-conflict.js +264 -0
- package/dist/utils/path.d.ts +52 -0
- package/dist/utils/path.js +183 -0
- package/dist/utils/prettier-config.d.ts +2 -0
- package/dist/utils/prettier-config.js +13 -0
- package/dist/utils/prettier.d.ts +5 -0
- package/dist/utils/prettier.js +67 -0
- package/dist/utils/prettier.skiptest.d.ts +1 -0
- package/dist/utils/prettier.skiptest.js +22 -0
- package/dist/utils/promise.d.ts +2 -0
- package/dist/utils/promise.js +10 -0
- package/dist/utils/string-functions.d.ts +9 -0
- package/dist/utils/string-functions.js +23 -0
- package/dist/utils/sync-log-result.d.ts +9 -0
- package/dist/utils/sync-log-result.js +90 -0
- package/dist/utils/sync.d.ts +143 -0
- package/dist/utils/sync.js +325 -0
- package/dist/utils/template.d.ts +66 -0
- package/dist/utils/template.js +159 -0
- package/dist/utils/vfs.class.d.ts +115 -0
- package/dist/utils/vfs.class.js +239 -0
- package/dist/utils/zip.d.ts +13 -0
- package/dist/utils/zip.js +40 -0
- package/package.json +57 -34
- package/dist/generator.d.ts +0 -13
- package/dist/generator.js +0 -455
- package/dist/generators/enums/react.generator.d.ts +0 -10
- package/dist/generators/enums/react.generator.js +0 -110
- package/dist/generators/enums/types.generator.d.ts +0 -10
- package/dist/generators/enums/types.generator.js +0 -39
- package/dist/generators/indices/data/module.generator.d.ts +0 -9
- package/dist/generators/indices/data/module.generator.js +0 -60
- package/dist/generators/indices/data/service.generator.d.ts +0 -9
- package/dist/generators/indices/data/service.generator.js +0 -249
- package/dist/generators/indices/data/types.generator.d.ts +0 -9
- package/dist/generators/indices/data/types.generator.js +0 -49
- package/dist/generators/indices/dispatcher-service.generator.d.ts +0 -9
- package/dist/generators/indices/dispatcher-service.generator.js +0 -107
- package/dist/generators/indices/export/class.generator.d.ts +0 -9
- package/dist/generators/indices/export/class.generator.js +0 -140
- package/dist/generators/indices/export/encoder.generator.d.ts +0 -9
- package/dist/generators/indices/export/encoder.generator.js +0 -50
- package/dist/generators/indices/import/convert-functions.generator.d.ts +0 -9
- package/dist/generators/indices/import/convert-functions.generator.js +0 -509
- package/dist/generators/indices/import/decoder.generator.d.ts +0 -9
- package/dist/generators/indices/import/decoder.generator.js +0 -40
- package/dist/generators/indices/import/service.generator.d.ts +0 -9
- package/dist/generators/indices/import/service.generator.js +0 -573
- package/dist/generators/indices/import/types.generator.d.ts +0 -9
- package/dist/generators/indices/import/types.generator.js +0 -242
- package/dist/generators/indices/repositories.generator.d.ts +0 -9
- package/dist/generators/indices/repositories.generator.js +0 -25
- package/dist/generators/indices/routes.generator.d.ts +0 -9
- package/dist/generators/indices/routes.generator.js +0 -29
- package/dist/generators/indices/seed-migration.generator.d.ts +0 -9
- package/dist/generators/indices/seed-migration.generator.js +0 -36
- package/dist/generators/indices/seed-template.generator.d.ts +0 -9
- package/dist/generators/indices/seed-template.generator.js +0 -80
- package/dist/generators/indices/testids.generator.d.ts +0 -7
- package/dist/generators/indices/testids.generator.js +0 -71
- package/dist/generators/indices/types.generator.d.ts +0 -10
- package/dist/generators/indices/types.generator.js +0 -35
- package/dist/generators/indices/update/actiontypes.generator.d.ts +0 -9
- package/dist/generators/indices/update/actiontypes.generator.js +0 -49
- package/dist/generators/indices/update/module.generator.d.ts +0 -9
- package/dist/generators/indices/update/module.generator.js +0 -41
- package/dist/generators/indices/update/service.generator.d.ts +0 -9
- package/dist/generators/indices/update/service.generator.js +0 -34
- package/dist/generators/indices/view/module.generator.d.ts +0 -9
- package/dist/generators/indices/view/module.generator.js +0 -39
- package/dist/generators/indices/view/service.generator.d.ts +0 -9
- package/dist/generators/indices/view/service.generator.js +0 -34
- package/dist/generators/models/admin.page.generator.d.ts +0 -7
- package/dist/generators/models/admin.page.generator.js +0 -74
- package/dist/generators/models/export/encoder.generator.d.ts +0 -9
- package/dist/generators/models/export/encoder.generator.js +0 -51
- package/dist/generators/models/import/decoder.generator.d.ts +0 -9
- package/dist/generators/models/import/decoder.generator.js +0 -148
- package/dist/generators/models/react/context.generator.d.ts +0 -9
- package/dist/generators/models/react/context.generator.js +0 -71
- package/dist/generators/models/react/index.d.ts +0 -10
- package/dist/generators/models/react/index.js +0 -31
- package/dist/generators/models/react/library.generator.d.ts +0 -10
- package/dist/generators/models/react/library.generator.js +0 -94
- package/dist/generators/models/react/lookup.generator.d.ts +0 -9
- package/dist/generators/models/react/lookup.generator.js +0 -175
- package/dist/generators/models/react/modals.generator.d.ts +0 -23
- package/dist/generators/models/react/modals.generator.js +0 -710
- package/dist/generators/models/repository.generator.d.ts +0 -9
- package/dist/generators/models/repository.generator.js +0 -955
- package/dist/generators/models/route.generator.d.ts +0 -9
- package/dist/generators/models/route.generator.js +0 -92
- package/dist/generators/models/seed.generator.d.ts +0 -21
- package/dist/generators/models/seed.generator.js +0 -285
- package/dist/generators/models/stub.generator.d.ts +0 -9
- package/dist/generators/models/stub.generator.js +0 -92
- package/dist/generators/models/types.generator.d.ts +0 -9
- package/dist/generators/models/types.generator.js +0 -125
- package/dist/generators/models/update/service.generator.d.ts +0 -10
- package/dist/generators/models/update/service.generator.js +0 -302
- package/dist/generators/models/view/service.generator.d.ts +0 -10
- package/dist/generators/models/view/service.generator.js +0 -239
- package/dist/lib/attributes.d.ts +0 -114
- package/dist/lib/attributes.js +0 -2
- package/dist/lib/exports.d.ts +0 -45
- package/dist/lib/exports.js +0 -90
- package/dist/lib/imports.d.ts +0 -65
- package/dist/lib/imports.js +0 -114
- package/dist/lib/meta.d.ts +0 -1191
- package/dist/lib/meta.js +0 -434
- package/dist/lib/schema/fields.d.ts +0 -46
- package/dist/lib/schema/fields.js +0 -62
- package/dist/lib/schema/schema.d.ts +0 -466
- package/dist/lib/schema/schema.js +0 -18
- package/dist/lib/schema/types.d.ts +0 -201
- package/dist/lib/schema/types.js +0 -112
- package/dist/lib/serializer.d.ts +0 -15
- package/dist/lib/serializer.js +0 -24
- package/dist/lib/test-id-collector.d.ts +0 -42
- package/dist/lib/test-id-collector.js +0 -53
- package/dist/lib/types.d.ts +0 -7
- package/dist/lib/types.js +0 -13
- package/dist/lib/typescript.d.ts +0 -5
- package/dist/lib/typescript.js +0 -22
- package/dist/lib/utils/ast.d.ts +0 -29
- package/dist/lib/utils/ast.js +0 -23
- package/dist/lib/utils/error.d.ts +0 -17
- package/dist/lib/utils/error.js +0 -52
- package/dist/lib/utils/file.d.ts +0 -10
- package/dist/lib/utils/file.js +0 -56
- package/dist/lib/utils/jsdoc.d.ts +0 -9
- package/dist/lib/utils/jsdoc.js +0 -37
- package/dist/lib/utils/logger.d.ts +0 -17
- package/dist/lib/utils/logger.js +0 -12
- package/dist/lib/utils/string.d.ts +0 -40
- package/dist/lib/utils/string.js +0 -187
- package/dist/lib/utils/types.d.ts +0 -12
- package/dist/lib/utils/types.js +0 -2
- package/dist/lib/zod.d.ts +0 -8
- package/dist/lib/zod.js +0 -60
- package/dist/prisma/attributes.d.ts +0 -21
- package/dist/prisma/attributes.js +0 -175
- package/dist/prisma/client-path.d.ts +0 -7
- package/dist/prisma/client-path.js +0 -29
- package/dist/prisma/parse.d.ts +0 -12
- package/dist/prisma/parse.js +0 -452
|
@@ -1,955 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateRepository = void 0;
|
|
4
|
-
const imports_1 = require("../../lib/imports");
|
|
5
|
-
const meta_1 = require("../../lib/meta");
|
|
6
|
-
const fields_1 = require("../../lib/schema/fields");
|
|
7
|
-
const schema_1 = require("../../lib/schema/schema");
|
|
8
|
-
const types_1 = require("../../lib/schema/types");
|
|
9
|
-
const types_2 = require("../../lib/types");
|
|
10
|
-
const jsdoc_1 = require("../../lib/utils/jsdoc");
|
|
11
|
-
const string_1 = require("../../lib/utils/string");
|
|
12
|
-
/**
|
|
13
|
-
* Generates repository data structure for a given model.
|
|
14
|
-
*/
|
|
15
|
-
function generateRepository({ model, meta }) {
|
|
16
|
-
const { idField } = model;
|
|
17
|
-
const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
|
|
18
|
-
const imports = imports_1.ImportsGenerator.from(meta.data.repository.location.path).addImports({
|
|
19
|
-
[schemaMeta.data.repository.typeFilePath]: (0, types_1.toAnnotatedTypeName)(schemaMeta.data.repository.typeName),
|
|
20
|
-
[meta.types.importPath]: [
|
|
21
|
-
(0, types_1.toAnnotatedTypeName)(model.typeName),
|
|
22
|
-
(0, types_1.toAnnotatedTypeName)(model.brandedIdType),
|
|
23
|
-
meta.types.toBrandedIdTypeFnName,
|
|
24
|
-
meta.types.dto.create,
|
|
25
|
-
meta.types.dto.update,
|
|
26
|
-
meta.types.dto.upsert,
|
|
27
|
-
meta.types.dto.clone,
|
|
28
|
-
],
|
|
29
|
-
[schemaMeta.actions.execution.interfaceLocation.import]: [schemaMeta.actions.execution.interface],
|
|
30
|
-
});
|
|
31
|
-
const idBlocks = generateIdBlocks({ model, meta });
|
|
32
|
-
const defaultValueBlocks = generateDefaultBlocks({ model, meta });
|
|
33
|
-
const uniqueStringFieldsBlocks = generateUniqueFieldsBlocks({ model, meta });
|
|
34
|
-
const maxLengthBlocks = generateMaxLengthBlocks({ model, meta });
|
|
35
|
-
const validationBlocks = generateValidationBlocks({ model, meta });
|
|
36
|
-
const indexBlocks = generateIndexBlocks({ model, imports });
|
|
37
|
-
const relationsBlocks = generateRelationsBlocks({ model, meta, imports });
|
|
38
|
-
imports.addImports({
|
|
39
|
-
[meta.types.importPath]: [meta.types.zodDecoderFnNames.fromDatabase],
|
|
40
|
-
[schemaMeta.backendModules.db.databaseService.location.import]: [schemaMeta.backendModules.db.databaseService.name],
|
|
41
|
-
[schemaMeta.backendModules.db.typesImportPath]: [(0, types_1.toAnnotatedTypeName)((0, types_1.toTypeName)(`${model.sourceName} as DbType`))],
|
|
42
|
-
[(0, types_1.toPackageName)('@postxl/runtime')]: [(0, types_1.toFunctionName)('format'), (0, types_1.toFunctionName)('pluralize')],
|
|
43
|
-
});
|
|
44
|
-
const userRepositorySpecificBlocks = generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports });
|
|
45
|
-
return `
|
|
46
|
-
import { Injectable, Logger } from '@nestjs/common'
|
|
47
|
-
${idBlocks.libraryImports}
|
|
48
|
-
${imports.generate()}
|
|
49
|
-
|
|
50
|
-
@Injectable()
|
|
51
|
-
export class ${meta.data.repository.className} implements Repository<${model.typeName}, ${idField.unbrandedTypeName}> {
|
|
52
|
-
protected data: Map<${model.brandedIdType}, ${model.typeName}> = new Map()
|
|
53
|
-
protected logger = new Logger(${meta.data.repository.className}.name)
|
|
54
|
-
|
|
55
|
-
${relationsBlocks.mapDeclarations.join('\n')}
|
|
56
|
-
|
|
57
|
-
${defaultValueBlocks.publicVariableDeclaration}
|
|
58
|
-
|
|
59
|
-
${idBlocks.generateNextIdFunctionName}
|
|
60
|
-
|
|
61
|
-
protected uniqueIds = {
|
|
62
|
-
${uniqueStringFieldsBlocks.mapDeclarations.join(',\n')}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
${indexBlocks.nestedMapDeclarations.join('\n')}
|
|
66
|
-
|
|
67
|
-
${userRepositorySpecificBlocks.rootUserNameConst}
|
|
68
|
-
|
|
69
|
-
${userRepositorySpecificBlocks.getterBlock}
|
|
70
|
-
|
|
71
|
-
constructor(protected db: ${schemaMeta.backendModules.db.databaseService.name}) {}
|
|
72
|
-
|
|
73
|
-
public async init() {
|
|
74
|
-
this.data.clear()
|
|
75
|
-
|
|
76
|
-
${relationsBlocks.clearCode.join('\n')}
|
|
77
|
-
|
|
78
|
-
${uniqueStringFieldsBlocks.clearCode.join('\n')}
|
|
79
|
-
|
|
80
|
-
${defaultValueBlocks.init.resetCode}
|
|
81
|
-
|
|
82
|
-
${indexBlocks.initCode.join('\n')}
|
|
83
|
-
|
|
84
|
-
const data = await this.db.${meta.data.repository.getMethodFnName}.findMany({})
|
|
85
|
-
|
|
86
|
-
for (const rawItem of data) {
|
|
87
|
-
const item = this.${meta.data.repository.decoderFnName}(rawItem)
|
|
88
|
-
this.set(item)
|
|
89
|
-
|
|
90
|
-
${defaultValueBlocks.init.setCode}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
${idBlocks.initCode}
|
|
94
|
-
|
|
95
|
-
${defaultValueBlocks.init.checkCode}
|
|
96
|
-
|
|
97
|
-
${userRepositorySpecificBlocks.initCall}
|
|
98
|
-
|
|
99
|
-
this.logger.log(\`\${format(this.data.size)} \${pluralize('${model.typeName}', this.data.size)} loaded\`)
|
|
100
|
-
${indexBlocks.initLogCode.join('\n')}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
${userRepositorySpecificBlocks.rootUserInitializeBlock}
|
|
104
|
-
|
|
105
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
106
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
107
|
-
public async get(id: ${model.brandedIdType} | null): Promise<${model.typeName} | null> {
|
|
108
|
-
if (id === null) {
|
|
109
|
-
return null
|
|
110
|
-
}
|
|
111
|
-
return this.data.get(id) ?? null
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
116
|
-
public async getAll(): Promise<Map<${model.brandedIdType}, ${model.typeName}>> {
|
|
117
|
-
return new Map(this.data)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
121
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
122
|
-
public async getAllAsArray(): Promise<${model.typeName}[]> {
|
|
123
|
-
return Array.from(this.data.values())
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
${indexBlocks.getterFunctions.join('\n')}
|
|
127
|
-
|
|
128
|
-
${uniqueStringFieldsBlocks.getByFunctions.join('\n')}
|
|
129
|
-
|
|
130
|
-
public async filter(predicate: (item: ${model.typeName}) => boolean): Promise<${model.typeName}[]> {
|
|
131
|
-
return (await this.getAllAsArray()).filter(predicate)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public async findFirst(predicate: (item: ${model.typeName}) => boolean): Promise<${model.typeName} | null> {
|
|
135
|
-
return (await this.getAllAsArray()).find(predicate) ?? null
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
140
|
-
public async count(): Promise<number> {
|
|
141
|
-
return this.data.size
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
${(0, jsdoc_1.toJsDocComment)([
|
|
145
|
-
`Checks that item has the ${idField.name} field.`,
|
|
146
|
-
`In case none exists, ${idBlocks.verifyFunctionComment}`,
|
|
147
|
-
uniqueStringFieldsBlocks.verifyFunctionComment,
|
|
148
|
-
maxLengthBlocks.verifyFunctionComment,
|
|
149
|
-
])}
|
|
150
|
-
private verifyItem(
|
|
151
|
-
item: ${idBlocks.verifyFunctionParameterType}
|
|
152
|
-
): ${idBlocks.createFunctionParameterType} {
|
|
153
|
-
${idBlocks.verifyCode}
|
|
154
|
-
|
|
155
|
-
${maxLengthBlocks.verifyCode.join('\n')}
|
|
156
|
-
|
|
157
|
-
${uniqueStringFieldsBlocks.verifyCode.join('\n')}
|
|
158
|
-
|
|
159
|
-
${validationBlocks.verifyCode.join('\n')}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
${idField.name},
|
|
163
|
-
${model.fields
|
|
164
|
-
.filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
|
|
165
|
-
.map((f) => `${f.name}: item.${f.name}`)
|
|
166
|
-
.join(',\n')}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private toCreateItem(item: ${idBlocks.createFunctionParameterType}) {
|
|
171
|
-
return {
|
|
172
|
-
${model.fields
|
|
173
|
-
.filter((f) => !f.attributes.isReadonly || f.kind === 'id')
|
|
174
|
-
.map((f) => `${f.sourceName}: item.${f.name}`)
|
|
175
|
-
.join(',\n')},
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} and returns it.`])}
|
|
180
|
-
public async create(
|
|
181
|
-
{ item, execution }: { item: ${meta.types.dto.create}, execution: ${schemaMeta.actions.execution.interface} }
|
|
182
|
-
): Promise<${model.typeName}> {
|
|
183
|
-
const mutationId = await execution.startCreateMutation({
|
|
184
|
-
model: '${meta.actions.actionScopeConstType}',
|
|
185
|
-
createObject: item
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const newItem = this.${meta.data.repository.decoderFnName}(
|
|
190
|
-
await this.db.${meta.data.repository.getMethodFnName}.create({
|
|
191
|
-
data: this.toCreateItem(this.verifyItem(item)),
|
|
192
|
-
}),
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
this.set(newItem)
|
|
196
|
-
await execution.finishCreateMutation({ mutationId, createdObject: newItem, entityId: newItem.id })
|
|
197
|
-
return newItem
|
|
198
|
-
} catch (error) {
|
|
199
|
-
await execution.errorMutation({ mutationId, error })
|
|
200
|
-
throw error
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
${(0, jsdoc_1.toJsDocComment)([`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
205
|
-
public async createMany(
|
|
206
|
-
{items, execution}: { items: ${meta.types.dto.create}[], execution: ${schemaMeta.actions.execution.interface} }
|
|
207
|
-
): Promise<${model.typeName}[]> {
|
|
208
|
-
const mutationId = await execution.startCreateManyMutation({
|
|
209
|
-
model: '${meta.actions.actionScopeConstType}',
|
|
210
|
-
createObjects: items
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
const newItems = items.map((item) => this.verifyItem(item))
|
|
215
|
-
|
|
216
|
-
await this.db.${meta.data.repository.getMethodFnName}.createMany({ data: newItems.map(i => this.toCreateItem(i)) })
|
|
217
|
-
|
|
218
|
-
const dbItems = await this.db.${meta.data.repository.getMethodFnName}.findMany({
|
|
219
|
-
where: {
|
|
220
|
-
${model.idField.sourceName}: { in: newItems.map(i => i.${model.idField.name}) }
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
const result = dbItems.map((item) => this.${meta.data.repository.decoderFnName}(item))
|
|
225
|
-
|
|
226
|
-
for (const item of result) {
|
|
227
|
-
this.set(item)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
await execution.finishCreateManyMutation({
|
|
231
|
-
mutationId,
|
|
232
|
-
createdObjects: result,
|
|
233
|
-
entityIds: newItems.map((i) => i.id),
|
|
234
|
-
})
|
|
235
|
-
return result
|
|
236
|
-
} catch (error) {
|
|
237
|
-
await execution.errorMutation({ mutationId, error })
|
|
238
|
-
throw error
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
${(0, jsdoc_1.toJsDocComment)([`Updates a ${meta.userFriendlyName} and returns it.`])}
|
|
243
|
-
public async update(
|
|
244
|
-
{ item, execution }: { item: ${meta.types.dto.update}, execution: ${schemaMeta.actions.execution.interface} }
|
|
245
|
-
): Promise<${model.typeName}> {
|
|
246
|
-
const existingItem = await this.get(item.${idField.name})
|
|
247
|
-
if (!existingItem) {
|
|
248
|
-
throw new Error(\`Could not update ${meta.userFriendlyName} with id \${item.id}. Not found!\`)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const mutationId = await execution.startUpdateMutation({
|
|
252
|
-
model: '${meta.actions.actionScopeConstType}',
|
|
253
|
-
entityId: item.id,
|
|
254
|
-
sourceObject: existingItem,
|
|
255
|
-
updateObject: item,
|
|
256
|
-
})
|
|
257
|
-
try {
|
|
258
|
-
${maxLengthBlocks.updateCode.join('\n')}
|
|
259
|
-
|
|
260
|
-
${uniqueStringFieldsBlocks.updateCode.join('\n')}
|
|
261
|
-
|
|
262
|
-
const newItem = this.${meta.data.repository.decoderFnName}(
|
|
263
|
-
await this.db.${meta.data.repository.getMethodFnName}.update({
|
|
264
|
-
where: {
|
|
265
|
-
${idField.sourceName}: item.${idField.name},
|
|
266
|
-
},
|
|
267
|
-
data: {
|
|
268
|
-
${[...model.fields.values()]
|
|
269
|
-
.filter((f) => f.kind !== 'id' && !f.attributes.isReadonly)
|
|
270
|
-
.map((f) => f.isRequired
|
|
271
|
-
? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
|
|
272
|
-
: `${f.sourceName}: item.${f.name}`)
|
|
273
|
-
.join(',\n')}
|
|
274
|
-
},
|
|
275
|
-
}),
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
this.remove(existingItem)
|
|
279
|
-
this.set(newItem)
|
|
280
|
-
|
|
281
|
-
await execution.finishUpdateMutation({ mutationId, updatedObject: newItem })
|
|
282
|
-
return newItem
|
|
283
|
-
} catch (error) {
|
|
284
|
-
await execution.errorMutation({ mutationId, error })
|
|
285
|
-
throw error
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
${(0, jsdoc_1.toJsDocComment)([`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
290
|
-
public async updateMany(
|
|
291
|
-
{ items, execution }: { items: ${meta.types.dto.update}[], execution: ${schemaMeta.actions.execution.interface} }
|
|
292
|
-
): Promise<${model.typeName}[]> {
|
|
293
|
-
const result: ${model.typeName}[] = []
|
|
294
|
-
for (const item of items) {
|
|
295
|
-
try {
|
|
296
|
-
const updated = await this.update({ item, execution })
|
|
297
|
-
result.push(updated)
|
|
298
|
-
} catch {
|
|
299
|
-
/* empty */
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
return result
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
${(0, jsdoc_1.toJsDocComment)([`Creates or updates a ${meta.userFriendlyName} and returns it.`])}
|
|
306
|
-
public async upsert(
|
|
307
|
-
{ item, execution }: { item: ${meta.types.dto.upsert}, execution: ${schemaMeta.actions.execution.interface} }
|
|
308
|
-
): Promise<${model.typeName}> {
|
|
309
|
-
const existingItem = item.${model.idField.name} ? (await this.get(item.${model.idField.name})) : null
|
|
310
|
-
if (existingItem) {
|
|
311
|
-
return this.update({ item: item as ${meta.types.dto.update}, execution })
|
|
312
|
-
} else {
|
|
313
|
-
return this.create({ item: item as ${meta.types.dto.create}, execution })
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
${(0, jsdoc_1.toJsDocComment)([`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
318
|
-
public async upsertMany(
|
|
319
|
-
{ items, execution }: { items: ${meta.types.dto.upsert}[], execution: ${schemaMeta.actions.execution.interface} }
|
|
320
|
-
): Promise<${model.typeName}[]> {
|
|
321
|
-
const result: ${model.typeName}[] = []
|
|
322
|
-
for (const item of items) {
|
|
323
|
-
try {
|
|
324
|
-
const updated = await this.upsert({ item, execution })
|
|
325
|
-
result.push(updated)
|
|
326
|
-
} catch {
|
|
327
|
-
/* empty */
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return result
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
${(0, jsdoc_1.toJsDocComment)([`Deletes a ${meta.userFriendlyName} and returns its id.`])}
|
|
334
|
-
public async delete(
|
|
335
|
-
{ id, execution }: { id: ${model.brandedIdType}, execution: ${schemaMeta.actions.execution.interface} }
|
|
336
|
-
): Promise<${model.brandedIdType}> {
|
|
337
|
-
const existingItem = await this.get(id)
|
|
338
|
-
if (!existingItem) {
|
|
339
|
-
throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const mutationId = await execution.startDeleteMutation({
|
|
343
|
-
model: '${meta.actions.actionScopeConstType}',
|
|
344
|
-
entityId: id,
|
|
345
|
-
sourceObject: existingItem,
|
|
346
|
-
})
|
|
347
|
-
try {
|
|
348
|
-
|
|
349
|
-
await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
|
|
350
|
-
|
|
351
|
-
this.remove(existingItem)
|
|
352
|
-
await execution.finishDeleteMutation({ mutationId })
|
|
353
|
-
return id
|
|
354
|
-
} catch (error) {
|
|
355
|
-
await execution.errorMutation({ mutationId, error })
|
|
356
|
-
throw error
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
${(0, jsdoc_1.toJsDocComment)([`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`])}
|
|
361
|
-
public async deleteMany(
|
|
362
|
-
{ ids, execution }: { ids: ${model.brandedIdType}[], execution: ${schemaMeta.actions.execution.interface} }
|
|
363
|
-
): Promise<${model.brandedIdType}[]> {
|
|
364
|
-
const deletedIds: ${model.brandedIdType}[] = []
|
|
365
|
-
for (const id of ids) {
|
|
366
|
-
try {
|
|
367
|
-
await this.delete({ id, execution })
|
|
368
|
-
deletedIds.push(id)
|
|
369
|
-
} catch {
|
|
370
|
-
/* empty */
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return deletedIds
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} shallow clone and returns it.`])}
|
|
377
|
-
public async clone({ id, item, execution }: {
|
|
378
|
-
id: ${model.brandedIdType}
|
|
379
|
-
item: (item: ${model.typeName}) => ${meta.types.dto.clone},
|
|
380
|
-
execution: ${schemaMeta.actions.execution.interface}
|
|
381
|
-
}): Promise<${model.typeName}> {
|
|
382
|
-
const source = await this.get(id)
|
|
383
|
-
if (!source) {
|
|
384
|
-
throw new Error(\`${meta.userFriendlyName} with id \${id} not found\`)
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
388
|
-
const { id: _, ...data } = { ...source, ...item(source) }
|
|
389
|
-
|
|
390
|
-
return await this.create({ item: data, execution })
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
${relationsBlocks.getterFunctions.join('\n')}
|
|
394
|
-
|
|
395
|
-
${maxLengthBlocks.ensureMaxLengthFunctions.join('\n')}
|
|
396
|
-
|
|
397
|
-
${uniqueStringFieldsBlocks.ensureUniqueFunctions.join('\n\n')}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Function that adds/updates a given item to the internal data store, indexes, etc.
|
|
401
|
-
*/
|
|
402
|
-
private set(item: ${model.typeName}): void {
|
|
403
|
-
${idBlocks.setCode}
|
|
404
|
-
|
|
405
|
-
this.data.set(item.id, item)
|
|
406
|
-
|
|
407
|
-
${uniqueStringFieldsBlocks.setCode.join('\n')}
|
|
408
|
-
|
|
409
|
-
${relationsBlocks.setCode.join('\n')}
|
|
410
|
-
|
|
411
|
-
${indexBlocks.setCode.join('\n')}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Function that removes a given item from the internal data store, indexes, etc.
|
|
416
|
-
*/
|
|
417
|
-
private remove(item: ${model.typeName}): void {
|
|
418
|
-
this.data.delete(item.id)
|
|
419
|
-
${uniqueStringFieldsBlocks.removeCode.join('\n')}
|
|
420
|
-
|
|
421
|
-
${relationsBlocks.removeCode.join('\n')}
|
|
422
|
-
|
|
423
|
-
${indexBlocks.removeCode.join('\n')}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Utility function that converts a given Database object to a TypeScript model instance
|
|
428
|
-
*/
|
|
429
|
-
private ${meta.data.repository.decoderFnName}(
|
|
430
|
-
item: Pick<DbType, ${model.fields.map((f) => `'${f.sourceName}'`).join(' | ')}>
|
|
431
|
-
): ${model.typeName} {
|
|
432
|
-
return ${meta.types.zodDecoderFnNames.fromDatabase}.parse({
|
|
433
|
-
${model.fields.map((f) => `${f.name}: item.${f.sourceName}`).join(',\n')}
|
|
434
|
-
})
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
`;
|
|
438
|
-
}
|
|
439
|
-
exports.generateRepository = generateRepository;
|
|
440
|
-
function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports, }) {
|
|
441
|
-
if (model.name !== 'User') {
|
|
442
|
-
return {
|
|
443
|
-
rootUserNameConst: '',
|
|
444
|
-
getterBlock: '',
|
|
445
|
-
initCall: '',
|
|
446
|
-
rootUserInitializeBlock: '',
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
const { rootUserId, rootUserValue } = generateSharedRootUserBlocks({ model, meta, imports });
|
|
450
|
-
return {
|
|
451
|
-
rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}(${rootUserId})`,
|
|
452
|
-
getterBlock: `
|
|
453
|
-
// We initialize the root user in the init() function
|
|
454
|
-
private _rootUser!: ${meta.types.typeName}
|
|
455
|
-
public get rootUser(): ${meta.types.typeName} {
|
|
456
|
-
return this._rootUser
|
|
457
|
-
}`,
|
|
458
|
-
initCall: `await this.initializeRootUser()`,
|
|
459
|
-
rootUserInitializeBlock: `
|
|
460
|
-
private async initializeRootUser(): Promise<void> {
|
|
461
|
-
const existingRootUser = await this.get(${meta.data.repository.className}.ROOT_USER_ID)
|
|
462
|
-
if (existingRootUser) {
|
|
463
|
-
this._rootUser = existingRootUser
|
|
464
|
-
return
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const rawUser = await this.db.user.create({
|
|
468
|
-
data: { ${rootUserValue} },
|
|
469
|
-
})
|
|
470
|
-
const newRootUser = this.toUser(rawUser)
|
|
471
|
-
this.set(newRootUser)
|
|
472
|
-
this._rootUser = newRootUser
|
|
473
|
-
}`,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function generateSharedRootUserBlocks({ model, meta, imports, }) {
|
|
477
|
-
var _a;
|
|
478
|
-
const providedDefault = model.attributes.systemUser;
|
|
479
|
-
const assignments = [];
|
|
480
|
-
let rootUserId = '';
|
|
481
|
-
for (const field of model.fields) {
|
|
482
|
-
let value = undefined;
|
|
483
|
-
if (providedDefault && field.name in providedDefault) {
|
|
484
|
-
value = providedDefault[field.name];
|
|
485
|
-
}
|
|
486
|
-
else if ((_a = field.attributes.examples) === null || _a === void 0 ? void 0 : _a.length) {
|
|
487
|
-
value = field.attributes.examples[0];
|
|
488
|
-
}
|
|
489
|
-
else if (!field.isRequired) {
|
|
490
|
-
value = null;
|
|
491
|
-
}
|
|
492
|
-
else if (field.kind === 'id') {
|
|
493
|
-
if (field.unbrandedTypeName === 'string') {
|
|
494
|
-
value = 'rootId';
|
|
495
|
-
}
|
|
496
|
-
else if (field.unbrandedTypeName === 'number') {
|
|
497
|
-
value = -1;
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
throw new Error(`Could not generate root user: Unsupported id type ${field.unbrandedTypeName}!`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
if (field.isRequired && !field.attributes.isCreatedAt && !field.attributes.isUpdatedAt) {
|
|
505
|
-
throw new Error(`Could not generate root user: No value for field ${field.name} provided!`);
|
|
506
|
-
}
|
|
507
|
-
else {
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
switch (field.kind) {
|
|
512
|
-
case 'id': {
|
|
513
|
-
if (field.unbrandedTypeName === 'string') {
|
|
514
|
-
value = `'${value}'`;
|
|
515
|
-
}
|
|
516
|
-
else if (field.unbrandedTypeName === 'number') {
|
|
517
|
-
value = `${value}`;
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
throw new Error(`Could not generate root user: Unsupported id type ${field.unbrandedTypeName}!`);
|
|
521
|
-
}
|
|
522
|
-
rootUserId = value;
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
case 'scalar': {
|
|
526
|
-
if (value === null) {
|
|
527
|
-
break;
|
|
528
|
-
}
|
|
529
|
-
switch (field.tsTypeName) {
|
|
530
|
-
case 'string': {
|
|
531
|
-
value = `'${value}'`;
|
|
532
|
-
break;
|
|
533
|
-
}
|
|
534
|
-
case 'number': {
|
|
535
|
-
value = `${value}`;
|
|
536
|
-
break;
|
|
537
|
-
}
|
|
538
|
-
case 'boolean': {
|
|
539
|
-
value = value ? 'true' : 'false';
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
case 'Date': {
|
|
543
|
-
value = `${value}`;
|
|
544
|
-
break;
|
|
545
|
-
}
|
|
546
|
-
default: {
|
|
547
|
-
throw new Error(`Could not generate root user: Unsupported scalar type ${field.tsTypeName}!`);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
case 'relation': {
|
|
553
|
-
if (value === null) {
|
|
554
|
-
break;
|
|
555
|
-
}
|
|
556
|
-
if (field.unbrandedTypeName === 'string') {
|
|
557
|
-
value = `'${value}'`;
|
|
558
|
-
}
|
|
559
|
-
else if (field.unbrandedTypeName === 'number') {
|
|
560
|
-
value = `${value}`;
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
throw new Error(`Could not generate root user: Unsupported relation type ${field.unbrandedTypeName}!`);
|
|
564
|
-
}
|
|
565
|
-
break;
|
|
566
|
-
}
|
|
567
|
-
case 'enum': {
|
|
568
|
-
if (value === null) {
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
imports.addImport({
|
|
572
|
-
from: meta.types.importPath,
|
|
573
|
-
items: [field.enumerator.name],
|
|
574
|
-
});
|
|
575
|
-
value = `${field.enumerator.name}.${value}`;
|
|
576
|
-
break;
|
|
577
|
-
}
|
|
578
|
-
default: {
|
|
579
|
-
throw new types_2.ExhaustiveSwitchCheck(field);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
assignments.push(`${field.name}: ${value}`);
|
|
583
|
-
}
|
|
584
|
-
return {
|
|
585
|
-
rootUserId,
|
|
586
|
-
rootUserValue: assignments.join(', '),
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Generates code chunks responsible for verifying the ID validity of a model instance and generating the id
|
|
591
|
-
* value of a model with auto-generated id.
|
|
592
|
-
*/
|
|
593
|
-
function generateIdBlocks({ model, meta }) {
|
|
594
|
-
const idField = model.idField;
|
|
595
|
-
if (!idField.isGenerated) {
|
|
596
|
-
return {
|
|
597
|
-
libraryImports: '',
|
|
598
|
-
generateNextIdFunctionName: '',
|
|
599
|
-
initCode: '',
|
|
600
|
-
verifyFunctionComment: `an error is thrown as field has no default setting in schema.`,
|
|
601
|
-
verifyFunctionParameterType: meta.types.dto.create,
|
|
602
|
-
verifyCode: `
|
|
603
|
-
if (item.${idField.name} === undefined) {
|
|
604
|
-
throw new Error('Id field ${idField.name} is required!')
|
|
605
|
-
}
|
|
606
|
-
const ${idField.name} = ${meta.types.toBrandedIdTypeFnName}(item.${idField.name})`,
|
|
607
|
-
setCode: '',
|
|
608
|
-
createFunctionParameterType: model.typeName,
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
if (idField.schemaType === 'Int') {
|
|
612
|
-
const generatedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
|
|
613
|
-
const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
|
|
614
|
-
return {
|
|
615
|
-
libraryImports: '',
|
|
616
|
-
generateNextIdFunctionName: `
|
|
617
|
-
protected currentMaxId = 0
|
|
618
|
-
public generateNextId(): ${model.brandedIdType} {
|
|
619
|
-
return ${meta.types.toBrandedIdTypeFnName}(++this.currentMaxId)
|
|
620
|
-
}`,
|
|
621
|
-
initCode: `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`,
|
|
622
|
-
verifyFunctionComment: 'the id is generated by increasing the highest former id and assigned to the item.',
|
|
623
|
-
// prettier-ignore
|
|
624
|
-
verifyFunctionParameterType: `(Omit<${model.typeName}, ${generatedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
|
|
625
|
-
verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
|
|
626
|
-
createFunctionParameterType:
|
|
627
|
-
// NOTE: In case we have readonly fields, we need to omit them from the create function.
|
|
628
|
-
readonlyFields.length === 0
|
|
629
|
-
? model.typeName
|
|
630
|
-
: `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
|
|
631
|
-
setCode: `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }`,
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
if (idField.schemaType === 'String') {
|
|
635
|
-
const dbGeneratedFields = model.fields.filter((f) => f.kind === 'id' || f.attributes.isReadonly);
|
|
636
|
-
const readonlyFields = model.fields.filter((f) => f.attributes.isReadonly && f.kind !== 'id');
|
|
637
|
-
return {
|
|
638
|
-
libraryImports: `import { randomUUID } from 'crypto'`,
|
|
639
|
-
generateNextIdFunctionName: `
|
|
640
|
-
public generateNextId(): ${model.brandedIdType} {
|
|
641
|
-
return ${meta.types.toBrandedIdTypeFnName}(randomUUID())
|
|
642
|
-
}`,
|
|
643
|
-
initCode: '',
|
|
644
|
-
verifyFunctionComment: 'a new UUID is generated and assigned to the item.',
|
|
645
|
-
// prettier-ignore
|
|
646
|
-
verifyFunctionParameterType: `(Omit<${model.typeName}, ${dbGeneratedFields.map((f) => `'${f.name}'`).join(' | ')}> & Partial<{${idField.name}: ${idField.unbrandedTypeName}}>)`,
|
|
647
|
-
verifyCode: `const ${idField.name} = (item.${idField.name} !== undefined) ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : this.generateNextId()`,
|
|
648
|
-
createFunctionParameterType:
|
|
649
|
-
// NOTE: In case we have readonly fields, we need to omit them from the create function.
|
|
650
|
-
readonlyFields.length === 0
|
|
651
|
-
? model.typeName
|
|
652
|
-
: `Omit<${model.typeName}, ${readonlyFields.map((f) => `'${f.name}'`).join(' |')}>`,
|
|
653
|
-
setCode: '',
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
throw new Error(`Repository block only supports Id generation for number and strings! Found ${idField.schemaType} for model ${model.name} instead!`);
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Returns the code chunks that define the default value property and its initialization.
|
|
660
|
-
*/
|
|
661
|
-
function generateDefaultBlocks({ model, meta }) {
|
|
662
|
-
const defaultField = model.defaultField;
|
|
663
|
-
if (!defaultField) {
|
|
664
|
-
return {
|
|
665
|
-
init: {
|
|
666
|
-
resetCode: '',
|
|
667
|
-
setCode: '',
|
|
668
|
-
checkCode: '',
|
|
669
|
-
},
|
|
670
|
-
publicVariableDeclaration: '',
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
return {
|
|
674
|
-
init: {
|
|
675
|
-
resetCode: `
|
|
676
|
-
// We re-initialize the default value to undefined so the check for exactly one item does not fail upon re-initialization
|
|
677
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-non-null-assertion
|
|
678
|
-
this.defaultValue = undefined!
|
|
679
|
-
`,
|
|
680
|
-
setCode: `
|
|
681
|
-
if (item.${defaultField.name}) {
|
|
682
|
-
if (this.defaultValue) {
|
|
683
|
-
console.warn(\`More than one default ${meta.userFriendlyName} found! \${this.defaultValue.id} and \${item.id}\`)
|
|
684
|
-
}
|
|
685
|
-
this.defaultValue = item
|
|
686
|
-
}`,
|
|
687
|
-
checkCode: `
|
|
688
|
-
if (!this.defaultValue) {
|
|
689
|
-
throw new Error('No default ${meta.userFriendlyName} found!')
|
|
690
|
-
}`,
|
|
691
|
-
},
|
|
692
|
-
publicVariableDeclaration: `
|
|
693
|
-
// We can safely skip the assignment here as this is done in the init function
|
|
694
|
-
public defaultValue!: ${model.typeName}
|
|
695
|
-
`,
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Generates code chunks that enforce the uniqueness of a given field.
|
|
700
|
-
*/
|
|
701
|
-
function generateUniqueFieldsBlocks({ model }) {
|
|
702
|
-
const fields = model.fields.filter(fields_1.isUniqueStringField);
|
|
703
|
-
const result = {
|
|
704
|
-
mapDeclarations: [],
|
|
705
|
-
getByFunctions: [],
|
|
706
|
-
clearCode: [],
|
|
707
|
-
verifyFunctionComment: '',
|
|
708
|
-
verifyCode: [],
|
|
709
|
-
updateCode: [],
|
|
710
|
-
ensureUniqueFunctions: [],
|
|
711
|
-
setCode: [],
|
|
712
|
-
removeCode: [],
|
|
713
|
-
};
|
|
714
|
-
for (const f of fields) {
|
|
715
|
-
const returnType = `string${f.isRequired ? '' : ' | null'}`;
|
|
716
|
-
result.mapDeclarations.push(`'${f.name}': new Map<string, ${model.typeName}>()`);
|
|
717
|
-
result.getByFunctions.push(`
|
|
718
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
719
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
720
|
-
public async getBy${(0, string_1.toPascalCase)(f.name)}(${f.name}: ${returnType}): Promise<${model.typeName} | undefined> {
|
|
721
|
-
${f.isRequired ? '' : `if (${f.name} === null) { return undefined }`}
|
|
722
|
-
return this.uniqueIds.${f.name}.get(${(0, string_1.toCamelCase)(f.name)})
|
|
723
|
-
}`);
|
|
724
|
-
result.clearCode.push(`this.uniqueIds.${f.name}.clear()`);
|
|
725
|
-
result.verifyCode.push(`this.${getEnsureUniqueFnName(f)}(item)`);
|
|
726
|
-
result.updateCode.push(`
|
|
727
|
-
if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
|
|
728
|
-
this.${getEnsureUniqueFnName(f)}(item)
|
|
729
|
-
}`);
|
|
730
|
-
result.ensureUniqueFunctions.push(`
|
|
731
|
-
/**
|
|
732
|
-
* Utility function that ensures that the ${f.name} field is unique
|
|
733
|
-
*/
|
|
734
|
-
private ${getEnsureUniqueFnName(f)}(item: { ${f.name}?: ${returnType} }) {
|
|
735
|
-
if (!item.${f.name}) {
|
|
736
|
-
return
|
|
737
|
-
}
|
|
738
|
-
if (!this.uniqueIds.${f.name}.has(item.${f.name})) {
|
|
739
|
-
return
|
|
740
|
-
}
|
|
741
|
-
let counter = 1
|
|
742
|
-
|
|
743
|
-
let ${f.name}: string
|
|
744
|
-
const source${f.name} =${!f.attributes.maxLength ? `item.${f.name}` : `item.${f.name}.substring(0, ${f.attributes.maxLength - 5})`}
|
|
745
|
-
|
|
746
|
-
do {
|
|
747
|
-
${f.name} = \`\${source${f.name}} (\${++counter})\`
|
|
748
|
-
} while (this.uniqueIds.${f.name}.has(${f.name}))
|
|
749
|
-
|
|
750
|
-
this.logger.log(\`${model.typeName} ${f.name} "\${item.${f.name}}" already exists. Renaming to "\${${f.name}}")\`)
|
|
751
|
-
item.${f.name} = ${f.name}
|
|
752
|
-
}`);
|
|
753
|
-
if (f.isRequired) {
|
|
754
|
-
result.setCode.push(`this.uniqueIds.${f.name}.set(item.${f.name}, item)`);
|
|
755
|
-
result.removeCode.push(`this.uniqueIds.${f.name}.delete(item.${f.name})`);
|
|
756
|
-
}
|
|
757
|
-
else {
|
|
758
|
-
result.setCode.push(`if (item.${f.name} !== null) { this.uniqueIds.${f.name}.set(item.${f.name}, item) }`);
|
|
759
|
-
result.removeCode.push(`if (item.${f.name} !== null) { this.uniqueIds.${f.name}.delete(item.${f.name}) }`);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
if (fields.length > 1) {
|
|
763
|
-
result.verifyFunctionComment = `In case a value of the fields ${fields
|
|
764
|
-
.map((f) => f.name)
|
|
765
|
-
.join(', ')} is not unique, it is renamed.\n`;
|
|
766
|
-
}
|
|
767
|
-
else if (fields.length === 1) {
|
|
768
|
-
result.verifyFunctionComment = `In case the value of the field ${fields[0].name} is not unique, it is renamed.\n`;
|
|
769
|
-
}
|
|
770
|
-
return result;
|
|
771
|
-
}
|
|
772
|
-
function getEnsureUniqueFnName(field) {
|
|
773
|
-
return `ensureUnique${(0, string_1.toPascalCase)(field.name)}`;
|
|
774
|
-
}
|
|
775
|
-
function generateMaxLengthBlocks({ model }) {
|
|
776
|
-
const fields = model.fields.filter(fields_1.isMaxLengthStringField);
|
|
777
|
-
const result = {
|
|
778
|
-
verifyFunctionComment: '',
|
|
779
|
-
verifyCode: [],
|
|
780
|
-
updateCode: [],
|
|
781
|
-
ensureMaxLengthFunctions: [],
|
|
782
|
-
};
|
|
783
|
-
for (const f of fields) {
|
|
784
|
-
result.verifyCode.push(`this.${getEnsureMaxLengthFnName(f)}(item)`);
|
|
785
|
-
result.updateCode.push(`
|
|
786
|
-
if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
|
|
787
|
-
this.${getEnsureMaxLengthFnName(f)}(item)
|
|
788
|
-
}`);
|
|
789
|
-
result.ensureMaxLengthFunctions.push(`
|
|
790
|
-
/**
|
|
791
|
-
* Utility function that ensures that the ${f.name} field has a max length of ${f.attributes.maxLength}
|
|
792
|
-
*/
|
|
793
|
-
private ${getEnsureMaxLengthFnName(f)}(item: { ${f.name}?: string }) {
|
|
794
|
-
if (!item.${f.name}) {
|
|
795
|
-
return
|
|
796
|
-
}
|
|
797
|
-
if (item.${f.name}.length <= ${f.attributes.maxLength}) {
|
|
798
|
-
return
|
|
799
|
-
}
|
|
800
|
-
item.${f.name} = item.${f.name}.substring(0, ${f.attributes.maxLength - 4}) + \`...\`
|
|
801
|
-
}`);
|
|
802
|
-
}
|
|
803
|
-
if (fields.length > 1) {
|
|
804
|
-
result.verifyFunctionComment = `In case a value of the fields ${fields
|
|
805
|
-
.map((f) => f.name)
|
|
806
|
-
.join(', ')} exceeds its max length, it is truncated.\n`;
|
|
807
|
-
}
|
|
808
|
-
else if (fields.length === 1) {
|
|
809
|
-
result.verifyFunctionComment = `In case the value of the field ${fields[0].name} exceeds its max length, it is truncated.\n`;
|
|
810
|
-
}
|
|
811
|
-
return result;
|
|
812
|
-
}
|
|
813
|
-
function getEnsureMaxLengthFnName(field) {
|
|
814
|
-
return `ensureMaxLength${(0, string_1.toPascalCase)(field.name)}`;
|
|
815
|
-
}
|
|
816
|
-
function generateValidationBlocks({ model }) {
|
|
817
|
-
const fields = model.fields.filter((f) => f.kind === 'scalar' && f.validation && f.validation.type === 'int');
|
|
818
|
-
const result = {
|
|
819
|
-
verifyCode: [],
|
|
820
|
-
};
|
|
821
|
-
for (const f of fields) {
|
|
822
|
-
const itemExists = f.isRequired ? '' : `item.${f.name} !== null && `;
|
|
823
|
-
result.verifyCode.push(`
|
|
824
|
-
// ensure that ${f.name} is an integer
|
|
825
|
-
if (${itemExists}item.${f.name} !== Math.floor(item.${f.name})) {
|
|
826
|
-
throw new Error(\`Invalid value for field ${f.name}: \${item.${f.name}}. Value must be an integer.\`)
|
|
827
|
-
}`);
|
|
828
|
-
}
|
|
829
|
-
return result;
|
|
830
|
-
}
|
|
831
|
-
function generateIndexBlocks({ model, imports }) {
|
|
832
|
-
const indexes = model.attributes.index ? [getIndexDefinition({ fieldNames: model.attributes.index, model })] : [];
|
|
833
|
-
if (indexes.length > 0) {
|
|
834
|
-
imports.addImport({ items: [(0, types_1.toClassName)('NestedMap')], from: (0, types_1.toPackageName)('@postxl/runtime') });
|
|
835
|
-
}
|
|
836
|
-
const result = {
|
|
837
|
-
nestedMapDeclarations: [],
|
|
838
|
-
initCode: [],
|
|
839
|
-
initLogCode: [],
|
|
840
|
-
getterFunctions: [],
|
|
841
|
-
setCode: [],
|
|
842
|
-
removeCode: [],
|
|
843
|
-
};
|
|
844
|
-
for (const i of indexes) {
|
|
845
|
-
result.nestedMapDeclarations.push(`
|
|
846
|
-
protected ${i.name} = new NestedMap<${i.fields.map((f) => f.meta.types.brandedIdType).join(',')}, ${model.typeName}>(
|
|
847
|
-
${i.fields.map((f) => `(x) => x.${f.name}`).join(',')}
|
|
848
|
-
)`);
|
|
849
|
-
result.initCode.push(`this.${i.name}.clear()`);
|
|
850
|
-
result.initLogCode.push(`this.logger.log(\`\${this.${i.name}.size} ${model.typeName} loaded into index ${i.name}\`)`);
|
|
851
|
-
result.getterFunctions.push(`
|
|
852
|
-
public getFrom${indexes.length === 1 ? 'Index' : (0, string_1.toPascalCase)(i.name)}
|
|
853
|
-
({ ${i.fields.map((f) => f.name).join(',')} }: { ${i.fields
|
|
854
|
-
.map((f) => `${f.name} : ${f.meta.types.brandedIdType}`)
|
|
855
|
-
.join(';')} }): ${model.typeName} | null {
|
|
856
|
-
return this.${i.name}.get(${i.fields.map((f) => f.name).join(',')}) ?? null
|
|
857
|
-
}`);
|
|
858
|
-
result.setCode.push(`this.${i.name}.set(item)`);
|
|
859
|
-
result.removeCode.push(`this.${i.name}.delete(item)`);
|
|
860
|
-
}
|
|
861
|
-
return result;
|
|
862
|
-
}
|
|
863
|
-
function getIndexDefinition({ fieldNames, model }) {
|
|
864
|
-
const fields = fieldNames.map((f) => {
|
|
865
|
-
const result = model.fields.find((field) => field.name === f);
|
|
866
|
-
if (!result) {
|
|
867
|
-
throw new Error(`Index field ${f} not found in model ${model.name}`);
|
|
868
|
-
}
|
|
869
|
-
if (result.kind !== 'relation') {
|
|
870
|
-
throw new Error(`Index field ${f} is not a relation in model ${model.name}`);
|
|
871
|
-
}
|
|
872
|
-
const meta = (0, meta_1.getModelMetadata)({ model: result.relationToModel });
|
|
873
|
-
return Object.assign(Object.assign({}, result), { meta });
|
|
874
|
-
});
|
|
875
|
-
const [firstField, ...restFields] = fields;
|
|
876
|
-
if (!restFields || restFields.length === 0) {
|
|
877
|
-
throw new Error(`Index for model ${model.name} must have at least 2 fields!`);
|
|
878
|
-
}
|
|
879
|
-
return {
|
|
880
|
-
fields,
|
|
881
|
-
name: (0, string_1.toCamelCase)(`${firstField.name}${restFields.map((f) => (0, string_1.toPascalCase)(f.name)).join('')}Index`),
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
function generateRelationsBlocks({ model, imports, }) {
|
|
885
|
-
const relations = model.fields.filter(schema_1.isFieldRelation);
|
|
886
|
-
const result = {
|
|
887
|
-
mapDeclarations: [],
|
|
888
|
-
clearCode: [],
|
|
889
|
-
getterFunctions: [],
|
|
890
|
-
setCode: [],
|
|
891
|
-
removeCode: [],
|
|
892
|
-
};
|
|
893
|
-
for (const r of relations) {
|
|
894
|
-
const fieldMeta = (0, meta_1.getFieldMetadata)({ field: r });
|
|
895
|
-
const relationModelMeta = (0, meta_1.getModelMetadata)({ model: r.relationToModel });
|
|
896
|
-
imports.addTypeImport({
|
|
897
|
-
items: [relationModelMeta.types.brandedIdType],
|
|
898
|
-
from: relationModelMeta.types.importPath,
|
|
899
|
-
});
|
|
900
|
-
result.mapDeclarations.push(`protected ${r.name}Map: Map<${relationModelMeta.types.brandedIdType}, Map<${model.brandedIdType}, ${model.typeName}>> = new Map()`);
|
|
901
|
-
result.clearCode.push(`this.${r.name}Map.clear()`);
|
|
902
|
-
result.getterFunctions.push(`
|
|
903
|
-
/**
|
|
904
|
-
* Function to retrieve all ${(0, string_1.pluralize)(model.name)} that are related to a ${r.name}
|
|
905
|
-
*/
|
|
906
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
907
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
908
|
-
public async ${fieldMeta.getByForeignKeyMethodFnName}(
|
|
909
|
-
id: ${relationModelMeta.types.brandedIdType}
|
|
910
|
-
): Promise<Map<${model.brandedIdType}, ${model.typeName}>> {
|
|
911
|
-
const result = this.${r.name}Map.get(id)
|
|
912
|
-
if (!result) {
|
|
913
|
-
return new Map<${model.brandedIdType}, ${model.typeName}>()
|
|
914
|
-
}
|
|
915
|
-
return new Map(result)
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
/**
|
|
919
|
-
* Function to retrieve all ${model.brandedIdType}s that are related to a ${r.name}
|
|
920
|
-
*/
|
|
921
|
-
// NOTE: The current implementation is synchronous, but it needs to be async to conform to the interface.
|
|
922
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
923
|
-
public async ${fieldMeta.getByForeignKeyIdsMethodFnName}(
|
|
924
|
-
id: ${relationModelMeta.types.brandedIdType}
|
|
925
|
-
): Promise<${model.brandedIdType}[]> {
|
|
926
|
-
const s = this.${r.name}Map.get(id)
|
|
927
|
-
if (!s) {
|
|
928
|
-
return []
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
return Array.from(s.keys())
|
|
932
|
-
}`);
|
|
933
|
-
result.setCode.push(`
|
|
934
|
-
${!r.isRequired ? `if (item.${r.name}) {` : ''}
|
|
935
|
-
let ${r.name}Map = this.${r.name}Map.get(item.${r.name})
|
|
936
|
-
if (!${r.name}Map) {
|
|
937
|
-
${r.name}Map = new Map()
|
|
938
|
-
this.${r.name}Map.set(item.${r.name}, ${r.name}Map)
|
|
939
|
-
}
|
|
940
|
-
${r.name}Map.set(item.id, item)
|
|
941
|
-
${!r.isRequired ? `}` : ''}`);
|
|
942
|
-
result.removeCode.push(`
|
|
943
|
-
${!r.isRequired ? `if (item.${r.name}) {` : ''}
|
|
944
|
-
const ${r.name}Map = this.${r.name}Map.get(item.${r.name})
|
|
945
|
-
if (${r.name}Map) {
|
|
946
|
-
${r.name}Map.delete(item.id)
|
|
947
|
-
if (${r.name}Map.size === 0) {
|
|
948
|
-
this.${r.name}Map.delete(item.${r.name})
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
${!r.isRequired ? '}' : ''}
|
|
952
|
-
`);
|
|
953
|
-
}
|
|
954
|
-
return result;
|
|
955
|
-
}
|