@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, loadDataAfterSeeding: 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: { loadDataAfterSeeding, reset, log } }: { data: typeof SEEDDATA; options: SeedServiceOptions }) {
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)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {