@postxl/generator 0.22.2 → 0.23.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.
|
@@ -40,15 +40,9 @@ function generateSeedService({ models, meta }) {
|
|
|
40
40
|
* Note: This is significantly slower - but can be useful for debugging.
|
|
41
41
|
*/
|
|
42
42
|
log?: boolean
|
|
43
|
-
/**
|
|
44
|
-
* If true, data will be loaded into the repositories after seeding.
|
|
45
|
-
* This should be used on startup of the application, but typically
|
|
46
|
-
* not when running tests or from the CLI.
|
|
47
|
-
*/
|
|
48
|
-
loadDataAfterSeeding?: boolean
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
const DEFAULTOPTIONS: SeedServiceOptions = { reset: false, log: false
|
|
45
|
+
const DEFAULTOPTIONS: SeedServiceOptions = { reset: false, log: false }
|
|
52
46
|
@Injectable()
|
|
53
47
|
export class SeedService {
|
|
54
48
|
private readonly logger = new Logger(SeedService.name)
|
|
@@ -83,7 +77,7 @@ function generateSeedService({ models, meta }) {
|
|
|
83
77
|
return this._seed({ data: dataParsed.data, options })
|
|
84
78
|
}
|
|
85
79
|
|
|
86
|
-
private async _seed({ data, options: {
|
|
80
|
+
private async _seed({ data, options: { reset, log } }: { data: typeof SEEDDATA; options: SeedServiceOptions }) {
|
|
87
81
|
if (reset) {
|
|
88
82
|
this.logger.log('Resetting database')
|
|
89
83
|
await this.dbService.emptyDatabase()
|
|
@@ -105,10 +99,6 @@ function generateSeedService({ models, meta }) {
|
|
|
105
99
|
data: { isSeeded: true },
|
|
106
100
|
})
|
|
107
101
|
|
|
108
|
-
if (loadDataAfterSeeding) {
|
|
109
|
-
await this.dataService.init()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
102
|
this.logger.log('✅ Done')
|
|
113
103
|
}
|
|
114
104
|
|
|
@@ -116,6 +116,7 @@ function generateModelBusinessLogic({ model, meta }) {
|
|
|
116
116
|
`;
|
|
117
117
|
return `
|
|
118
118
|
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
119
|
+
import { FilterOperator } from '@pxl/common'
|
|
119
120
|
|
|
120
121
|
${imports.generate()}
|
|
121
122
|
|
|
@@ -140,6 +141,31 @@ export class ${meta.businessLogic.serviceClassName} {
|
|
|
140
141
|
return this.${modelRepositoryVariableName}.getAll()
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Returns a list of filtered, sorted and paginated ${meta.userFriendlyName}s.
|
|
146
|
+
*/
|
|
147
|
+
public getList({
|
|
148
|
+
filter,
|
|
149
|
+
sort,
|
|
150
|
+
cursor,
|
|
151
|
+
}: {
|
|
152
|
+
filter?: { field: keyof ${model.typeName}; operator: FilterOperator; value: string | number }
|
|
153
|
+
sort?: { field: keyof ${model.typeName}; ascending: boolean }
|
|
154
|
+
cursor?: { startRow: number; endRow: number }
|
|
155
|
+
}) {
|
|
156
|
+
const items = this.data.getAllAsArray()
|
|
157
|
+
const filtered = !filter
|
|
158
|
+
? items
|
|
159
|
+
: items.filter((item) => filterFn(item, filter.field, filter.operator, filter.value))
|
|
160
|
+
|
|
161
|
+
const filteredAndSorted = !sort
|
|
162
|
+
? filtered
|
|
163
|
+
: filtered.sort((a, b) => (sort.ascending ? compare(a, b, sort.field) : -compare(a, b, sort.field)))
|
|
164
|
+
|
|
165
|
+
const paginated = !cursor ? filteredAndSorted : filteredAndSorted.slice(cursor.startRow, cursor.endRow)
|
|
166
|
+
return { rows: paginated, count: items.length }
|
|
167
|
+
}
|
|
168
|
+
|
|
143
169
|
/**
|
|
144
170
|
* Creates a new ${meta.userFriendlyName}.
|
|
145
171
|
*/
|
|
@@ -164,6 +190,66 @@ export class ${meta.businessLogic.serviceClassName} {
|
|
|
164
190
|
*/
|
|
165
191
|
public async delete(id: ${model.brandedIdType}): Promise<void> {
|
|
166
192
|
return this.${modelRepositoryVariableName}.delete(id)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const compare = (a: ${model.typeName}, b: ${model.typeName}, field: keyof ${model.typeName}) => {
|
|
197
|
+
switch (field) {
|
|
198
|
+
${model.fields.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'string').length === 0
|
|
199
|
+
? ''
|
|
200
|
+
: `${model.fields
|
|
201
|
+
.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'string')
|
|
202
|
+
.map((f) => `case '${f.name}':`)
|
|
203
|
+
.join('\n')}
|
|
204
|
+
return (a[field] || '').localeCompare(b[field] || '')`}
|
|
205
|
+
default:
|
|
206
|
+
return 0
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const filterFn = (item: ${model.typeName}, field: keyof ${model.typeName}, operator: FilterOperator, value: string | number): boolean => {
|
|
211
|
+
switch (field) {
|
|
212
|
+
${model.fields.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'string').length === 0
|
|
213
|
+
? ''
|
|
214
|
+
: `${model.fields
|
|
215
|
+
.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'string')
|
|
216
|
+
.map((f) => `case '${f.name}':`)
|
|
217
|
+
.join('\n')}
|
|
218
|
+
{
|
|
219
|
+
if (typeof value !== 'string') return false
|
|
220
|
+
switch (operator) {
|
|
221
|
+
case 'contains': {
|
|
222
|
+
return (item[field] || '').toLowerCase().includes(value.toLowerCase())
|
|
223
|
+
}
|
|
224
|
+
default:
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
}`}
|
|
228
|
+
${model.fields.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'number').length === 0
|
|
229
|
+
? ''
|
|
230
|
+
: `${model.fields
|
|
231
|
+
.filter((f) => f.kind === 'scalar' && f.tsTypeName === 'number')
|
|
232
|
+
.map((f) => `case '${f.name}':`)
|
|
233
|
+
.join('\n')}
|
|
234
|
+
{
|
|
235
|
+
const toCompare = item[field]
|
|
236
|
+
if (typeof value !== 'number' || toCompare === null) return false
|
|
237
|
+
switch (operator) {
|
|
238
|
+
case 'eq': {
|
|
239
|
+
return item[field] === value
|
|
240
|
+
}
|
|
241
|
+
case 'gt': {
|
|
242
|
+
return toCompare > value
|
|
243
|
+
}
|
|
244
|
+
case 'lt': {
|
|
245
|
+
return toCompare < value
|
|
246
|
+
}
|
|
247
|
+
default:
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
250
|
+
}`}
|
|
251
|
+
default:
|
|
252
|
+
return false
|
|
167
253
|
}
|
|
168
254
|
}
|
|
169
255
|
`;
|
|
@@ -144,7 +144,7 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
144
144
|
${model.defaultField
|
|
145
145
|
? `
|
|
146
146
|
// We re-initialize the default value to undefined so the check for exactly one item does not fail upon re-initialization
|
|
147
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-non-null-assertion
|
|
148
148
|
this.defaultValue = undefined!
|
|
149
149
|
`
|
|
150
150
|
: ''}
|
|
@@ -17,7 +17,7 @@ function generateRoute({ model, meta }) {
|
|
|
17
17
|
const updateMethod = getUpdateMethod({ model, meta });
|
|
18
18
|
const deleteMethod = getDeleteMethod({ idField, meta });
|
|
19
19
|
const imports = imports_1.ImportsGenerator.from(meta.trpc.routerFilePath).addImport({
|
|
20
|
-
items: [meta.types.toBrandedIdTypeFnName],
|
|
20
|
+
items: [model.typeName, meta.types.toBrandedIdTypeFnName],
|
|
21
21
|
from: meta.types.importPath,
|
|
22
22
|
});
|
|
23
23
|
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
@@ -31,6 +31,7 @@ function generateRoute({ model, meta }) {
|
|
|
31
31
|
import { z } from 'zod'
|
|
32
32
|
import { procedure, router } from '../trpc'
|
|
33
33
|
|
|
34
|
+
import { CURSOR_DECODER, FILTER_OPERATOR_DECODER } from '@pxl/common'
|
|
34
35
|
${imports.generate()}
|
|
35
36
|
|
|
36
37
|
export const ${meta.trpc.routerName} = router({
|
|
@@ -40,6 +41,30 @@ export const ${meta.trpc.routerName} = router({
|
|
|
40
41
|
.input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
|
|
41
42
|
.query(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.get(input)),
|
|
42
43
|
getMap: procedure.query(({ ctx }) => ctx.data.${meta.data.dataServiceName}.getAll()),
|
|
44
|
+
getList: procedure
|
|
45
|
+
.input(z.object({
|
|
46
|
+
cursor: CURSOR_DECODER,
|
|
47
|
+
sort: z.object({
|
|
48
|
+
field: z.enum([
|
|
49
|
+
${model.fields.map((f) => `'${f.name}'`).join(',\n')}
|
|
50
|
+
])
|
|
51
|
+
.transform((v): keyof ${model.typeName} => v),
|
|
52
|
+
ascending: z.boolean(),
|
|
53
|
+
})
|
|
54
|
+
.optional(),
|
|
55
|
+
filter: z.object({
|
|
56
|
+
field: z.enum([
|
|
57
|
+
${model.fields.map((f) => `'${f.name}'`).join(',\n')}
|
|
58
|
+
])
|
|
59
|
+
.transform((v): keyof ${model.typeName} => v),
|
|
60
|
+
operator: FILTER_OPERATOR_DECODER,
|
|
61
|
+
value: z.union([z.string(), z.number()]),
|
|
62
|
+
})
|
|
63
|
+
.optional(),
|
|
64
|
+
}))
|
|
65
|
+
.query(({ input, ctx }) => {
|
|
66
|
+
return ctx.data.${meta.data.dataServiceName}.getList(input)
|
|
67
|
+
}),
|
|
43
68
|
|
|
44
69
|
${omit(createMethod, model.attributes.skipCreate)}
|
|
45
70
|
${omit(updateMethod, model.attributes.skipUpdate)}
|