@things-factory/operato-tools 8.0.0-beta.8 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1160 @@
1
+ import { Arg, Ctx, Query, Resolver } from 'type-graphql'
2
+ import { getRepository } from '@things-factory/shell'
3
+ import { Entity, EntityColumn } from '@things-factory/resource-base'
4
+
5
+ const { camelCase, startCase, snakeCase, kebabCase } = require('lodash')
6
+ const { plural } = require('pluralize')
7
+ const fs = require('fs')
8
+
9
+ @Resolver()
10
+ export class OperatoToolCreateService {
11
+ @Query(returns => Boolean, { description: 'Operato Tool Create Service' })
12
+ async toolCreateService(@Arg('id') id: string, @Ctx() context: any): Promise<Boolean> {
13
+ const { domain } = context.state
14
+ // Entity 조회
15
+ const entity: Entity = await getRepository(Entity).findOne({
16
+ where: {
17
+ id
18
+ }
19
+ })
20
+
21
+ // Entity 컬럼 조회
22
+ const entityColumns: EntityColumn[] = await getRepository(EntityColumn).find({
23
+ where: {
24
+ domain: { id: domain.id },
25
+ entity: { id: entity.id }
26
+ }
27
+ })
28
+
29
+ let { name = '', bundle = '' } = entity || {}
30
+
31
+ // 프로젝트 Root Path 구하기
32
+ let appRootPath: string = this.getProjectRootPath()
33
+ let serviceName: string = kebabCase(name)
34
+ // 서비스 경로 생성
35
+ await this.createServicePath(appRootPath, bundle, serviceName)
36
+
37
+ // 서비스 파일 생성
38
+ await this.createServiceFiles(appRootPath, bundle, name, serviceName, entity, entityColumns)
39
+
40
+ return true
41
+ }
42
+
43
+ /**
44
+ * 서비스 연관 파일 생성
45
+ * @param appRootPath
46
+ * @param moduleName
47
+ * @param name
48
+ * @param serviceName
49
+ * @param entity
50
+ * @param entityColumns
51
+ */
52
+ async createServiceFiles(
53
+ appRootPath: string,
54
+ moduleName: string,
55
+ name: string,
56
+ serviceName: string,
57
+ entity: Entity,
58
+ entityColumns: EntityColumn[]
59
+ ) {
60
+ // 이름 케이스 워드 만들기
61
+ let nameMap = {
62
+ name: serviceName,
63
+ tableName: entity.tableName,
64
+ pascalCaseName: startCase(camelCase(name)).replace(/ /g, ''),
65
+ camelCaseName: camelCase(name),
66
+ snakeCaseName: snakeCase(name),
67
+ pluralPascalCaseName: startCase(camelCase(plural(name))).replace(/ /g, ''),
68
+ pluralCamelCaseName: camelCase(plural(name))
69
+ }
70
+ let servicePath = `${appRootPath}/packages/${moduleName}/server/service/`
71
+
72
+ if (['JSON', 'COPY'].includes(entity.dataProp)) {
73
+ await this.createHistoryServiceFiles(servicePath, serviceName, entity, entityColumns, nameMap)
74
+ } else {
75
+ await this.createNoHistoryServiceFiels(servicePath, serviceName, entity, entityColumns, nameMap)
76
+ }
77
+ }
78
+
79
+ async createHistoryServiceFiles(
80
+ servicePath: string,
81
+ serviceName: string,
82
+ entity: Entity,
83
+ entityColumns: EntityColumn[],
84
+ nameMap: any
85
+ ) {
86
+ // 서비스 연관 파일 생성
87
+ await this.writeFile(servicePath + `${serviceName}/index.ts`, serviceHistoryIndex, nameMap)
88
+
89
+ // Entity
90
+ let entityText = this.createEntityText(entity, entityColumns)
91
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}.ts`, entityText, nameMap)
92
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-query.ts`, serviceQuery, nameMap)
93
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-mutation.ts`, serviceMutation, nameMap)
94
+ // TYPE
95
+ let typeText = this.createTypeText(entityColumns)
96
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-type.ts`, typeText, nameMap)
97
+
98
+ // 이력관리 entity 생성
99
+ let historyText = this.createHistoryEntityText(entity, entityColumns)
100
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-history.ts`, historyText, nameMap)
101
+ let entitySubscriberEntityToJson = ''
102
+ if (entity.dataProp == 'JSON') {
103
+ entitySubscriberEntityToJson = `
104
+ public createHistoryEntity(manager, entity) {
105
+ let history = manager.create(this.historyEntity, entity);
106
+ history.historyJson = JSON.stringify(entity);
107
+ return history;
108
+ }
109
+ `
110
+ await this.writeFile(servicePath + `${serviceName}/event-subscriber.ts`, serviceEntitySubscriber, nameMap)
111
+ } else {
112
+ entitySubscriberEntityToJson = ''
113
+ }
114
+ let subscriberTxt = serviceEntitySubscriber.replace(/{{entityToJson}}/g, entitySubscriberEntityToJson)
115
+ await this.writeFile(servicePath + `${serviceName}/event-subscriber.ts`, subscriberTxt, nameMap)
116
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-history-query.ts`, serviceHistoryQuery, nameMap)
117
+ // TYPE
118
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-history-type.ts`, sereviceHistoryType, nameMap)
119
+
120
+ // 전체 서비스 index.ts 파일
121
+ let allServiceIdxPath = servicePath + 'index.ts'
122
+ if ((await this.existsPath(allServiceIdxPath)) == false) {
123
+ await this.writeFile(allServiceIdxPath, allIndex, nameMap)
124
+ }
125
+
126
+ let allIdxFileTxt = await fs.readFileSync(allServiceIdxPath, 'utf8')
127
+
128
+ if (allIdxFileTxt.indexOf(`export * from './${nameMap.name}/${nameMap.name}-history'`) < 0) {
129
+ allIdxFileTxt = allIdxFileTxt.replace(
130
+ /\/\* EXPORT ENTITY TYPES \*\//,
131
+ `/* EXPORT ENTITY TYPES */\nexport * from './${nameMap.name}/${nameMap.name}-history'`
132
+ )
133
+ }
134
+
135
+ if (allIdxFileTxt.indexOf(`export * from './${nameMap.name}/${nameMap.name}'`) < 0) {
136
+ allIdxFileTxt = allIdxFileTxt.replace(
137
+ /\/\* EXPORT ENTITY TYPES \*\//,
138
+ `/* EXPORT ENTITY TYPES */\nexport * from './${nameMap.name}/${nameMap.name}'`
139
+ )
140
+ }
141
+
142
+ if (
143
+ allIdxFileTxt.indexOf(
144
+ `import { entities as ${nameMap.pascalCaseName}Entities, resolvers as ${nameMap.pascalCaseName}Resolvers, subscribers as ${nameMap.pascalCaseName}EntitySubscribers } from './${nameMap.name}'`
145
+ ) < 0
146
+ ) {
147
+ allIdxFileTxt = allIdxFileTxt.replace(
148
+ /\/\* IMPORT ENTITIES AND RESOLVERS \*\//,
149
+ `/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as ${nameMap.pascalCaseName}Entities, resolvers as ${nameMap.pascalCaseName}Resolvers, subscribers as ${nameMap.pascalCaseName}EntitySubscribers } from './${nameMap.name}'`
150
+ )
151
+ }
152
+
153
+ if (allIdxFileTxt.indexOf(`...${nameMap.pascalCaseName}Entities,`) < 0) {
154
+ allIdxFileTxt = allIdxFileTxt.replace(
155
+ /\/\* ENTITIES \*\//,
156
+ `/* ENTITIES */\n\t...${nameMap.pascalCaseName}Entities,`
157
+ )
158
+ }
159
+
160
+ if (allIdxFileTxt.indexOf(`...${nameMap.pascalCaseName}Resolvers,`) < 0) {
161
+ allIdxFileTxt = allIdxFileTxt.replace(
162
+ /\/\* RESOLVER CLASSES \*\//,
163
+ `/* RESOLVER CLASSES */\n\t\t...${nameMap.pascalCaseName}Resolvers,`
164
+ )
165
+ }
166
+
167
+ if (allIdxFileTxt.indexOf(`...${nameMap.pascalCaseName}EntitySubscribers,`) < 0) {
168
+ allIdxFileTxt = allIdxFileTxt.replace(
169
+ /\/\* SUBSCRIBERS \*\//,
170
+ `/* SUBSCRIBERS */\n\t\t...${nameMap.pascalCaseName}EntitySubscribers,`
171
+ )
172
+ }
173
+
174
+ await this.writeFile(allServiceIdxPath, allIdxFileTxt, nameMap)
175
+ }
176
+
177
+ async createNoHistoryServiceFiels(
178
+ servicePath: string,
179
+ serviceName: string,
180
+ entity: Entity,
181
+ entityColumns: EntityColumn[],
182
+ nameMap: any
183
+ ) {
184
+ // 서비스 연관 파일 생성
185
+ await this.writeFile(servicePath + `${serviceName}/index.ts`, serviceIndex, nameMap)
186
+
187
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-query.ts`, serviceQuery, nameMap)
188
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-mutation.ts`, serviceMutation, nameMap)
189
+
190
+ // Entity
191
+ let entityText = this.createEntityText(entity, entityColumns)
192
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}.ts`, entityText, nameMap)
193
+
194
+ // TYPE
195
+ let typeText = this.createTypeText(entityColumns)
196
+ await this.writeFile(servicePath + `${serviceName}/${serviceName}-type.ts`, typeText, nameMap)
197
+
198
+ // 전체 서비스 index.ts 파일
199
+ let allServiceIdxPath = servicePath + 'index.ts'
200
+ if ((await this.existsPath(allServiceIdxPath)) == false) {
201
+ await this.writeFile(allServiceIdxPath, allIndex, nameMap)
202
+ }
203
+
204
+ let allIdxFileTxt = await fs.readFileSync(allServiceIdxPath, 'utf8')
205
+
206
+ if (allIdxFileTxt.indexOf(`export * from './${nameMap.name}/${nameMap.name}'`) < 0) {
207
+ allIdxFileTxt = allIdxFileTxt.replace(
208
+ /\/\* EXPORT ENTITY TYPES \*\//,
209
+ `/* EXPORT ENTITY TYPES */\nexport * from './${nameMap.name}/${nameMap.name}'`
210
+ )
211
+ }
212
+
213
+ if (
214
+ allIdxFileTxt.indexOf(
215
+ `import { entities as ${nameMap.pascalCaseName}Entities, resolvers as ${nameMap.pascalCaseName}Resolvers } from './${nameMap.name}'`
216
+ ) < 0
217
+ ) {
218
+ allIdxFileTxt = allIdxFileTxt.replace(
219
+ /\/\* IMPORT ENTITIES AND RESOLVERS \*\//,
220
+ `/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as ${nameMap.pascalCaseName}Entities, resolvers as ${nameMap.pascalCaseName}Resolvers } from './${nameMap.name}'`
221
+ )
222
+ }
223
+
224
+ if (allIdxFileTxt.indexOf(`...${nameMap.pascalCaseName}Entities,`) < 0) {
225
+ allIdxFileTxt = allIdxFileTxt.replace(
226
+ /\/\* ENTITIES \*\//,
227
+ `/* ENTITIES */\n\t...${nameMap.pascalCaseName}Entities,`
228
+ )
229
+ }
230
+
231
+ if (allIdxFileTxt.indexOf(`...${nameMap.pascalCaseName}Resolvers,`) < 0) {
232
+ allIdxFileTxt = allIdxFileTxt.replace(
233
+ /\/\* RESOLVER CLASSES \*\//,
234
+ `/* RESOLVER CLASSES */\n\t\t...${nameMap.pascalCaseName}Resolvers,`
235
+ )
236
+ }
237
+
238
+ await this.writeFile(allServiceIdxPath, allIdxFileTxt, nameMap)
239
+ }
240
+
241
+ /**
242
+ * 엔티티 타입 텍스트
243
+ * @param entityColumns
244
+ * @returns
245
+ */
246
+ createTypeText(entityColumns: EntityColumn[]) {
247
+ let typeText = sereviceType
248
+
249
+ let newTypeColumns = entityColumns.map(column => {
250
+ if (column.name == 'id') return ''
251
+ if (column.name == 'domain_id') return ''
252
+ if (column.name == 'created_at') return ''
253
+ if (column.name == 'updated_at') return ''
254
+ if (column.name == 'creator_id') return ''
255
+ if (column.name == 'updater_id') return ''
256
+
257
+ let fieldType: string = this.getFieldType(column)
258
+ let colTxtType: string = this.getColTxtType(column)
259
+
260
+ let colName = camelCase(column.name)
261
+ let fieldAnn = `@Field(${fieldType.length == 0 ? '' : 'type =>' + fieldType + ','} { nullable: ${column.nullable} })`
262
+ let columnTxt = `${colName}${column.nullable ? '?' : ''}: ${colTxtType}`
263
+
264
+ return ' ' + fieldAnn + '\n ' + columnTxt + '\n'
265
+ })
266
+
267
+ typeText = typeText.replace(/{{newTypeColumns}}/g, newTypeColumns.join('\n'))
268
+
269
+ let patchTypeColumns = entityColumns.map(column => {
270
+ if (column.name == 'id') return ''
271
+ if (column.name == 'domain_id') return ''
272
+ if (column.name == 'created_at') return ''
273
+ if (column.name == 'updated_at') return ''
274
+ if (column.name == 'creator_id') return ''
275
+ if (column.name == 'updater_id') return ''
276
+
277
+ let fieldType: string = this.getFieldType(column)
278
+ let colTxtType: string = this.getColTxtType(column)
279
+
280
+ // patch ?
281
+ column.nullable = true
282
+
283
+ let colName = camelCase(column.name)
284
+ let fieldAnn = `@Field(${fieldType.length == 0 ? '' : 'type =>' + fieldType + ','} { nullable: ${column.nullable} })`
285
+ let columnTxt = `${colName}${column.nullable ? '?' : ''}: ${colTxtType}`
286
+
287
+ return ' ' + fieldAnn + '\n ' + columnTxt + '\n'
288
+ })
289
+
290
+ typeText = typeText.replace(/{{patchTypeColumns}}/g, patchTypeColumns.join('\n'))
291
+
292
+ return typeText
293
+ }
294
+
295
+ /**
296
+ * 엔티티 생성 텍스트
297
+ * @param entity
298
+ * @param entityColumns
299
+ * @returns
300
+ */
301
+ createEntityText(entity: Entity, entityColumns: EntityColumn[]) {
302
+ let entityText = serviceEntity
303
+
304
+ // 컬럼 정렬
305
+ let sortedCols: EntityColumn[] = entityColumns.sort(function (a, b) {
306
+ return a.rank - b.rank
307
+ })
308
+
309
+ // 컬럼 정보 txt 변환
310
+ let entityColsText: string[] = sortedCols.map(x => {
311
+ return this.columnToEntityColumn(x)
312
+ })
313
+
314
+ if (['COPY', 'JSON'].includes(entity.dataProp)) {
315
+ entityColsText.push(`
316
+ @VersionColumn({ default: 1 })
317
+ @Field({ nullable: true })
318
+ dataRevisionNo?: number = 1`)
319
+ }
320
+
321
+ entityText = entityText.replace(/{{entityColumns}}/g, entityColsText.join('\n'))
322
+
323
+ // index 정보
324
+ let indexAnn = ``
325
+ let sortedIdxCols: EntityColumn[] = entityColumns
326
+ .filter(x => x.uniqRank && x.uniqRank > 0)
327
+ .sort(function (a, b) {
328
+ return a.uniqRank - b.uniqRank
329
+ })
330
+
331
+ if (sortedIdxCols.length > 0) {
332
+ indexAnn = `@Index('ix_{{snakeCase name}}_0', ({{camelCase name}}: {{pascalCase name}}) => [{{indexColumns}}], { unique: true })`
333
+
334
+ let idxColumns = sortedIdxCols.map(x => {
335
+ let colName = x.name
336
+ if (x.name == 'domain_id') colName = 'domain'
337
+ if (x.name == 'creator_id') colName = 'creator'
338
+ if (x.name == 'updater_id') colName = 'updater'
339
+
340
+ return '{{camelCase name}}.' + camelCase(colName)
341
+ })
342
+
343
+ indexAnn = indexAnn.replace(/{{indexColumns}}/g, idxColumns.join(','))
344
+ }
345
+ entityText = entityText.replace(/{{indexAnn}}/g, indexAnn)
346
+
347
+ return entityText
348
+ }
349
+
350
+ /**
351
+ * 이력관리 엔티티 생성 텍스트
352
+ * @param entity
353
+ * @param entityColumns
354
+ * @returns
355
+ */
356
+ createHistoryEntityText(entity: Entity, entityColumns: EntityColumn[]) {
357
+ let entityText = entity.dataProp == 'JSON' ? serviceEntityHistJson : serviceEntityHistCopy
358
+
359
+ // 컬럼 정렬
360
+ let sortedCols: EntityColumn[] = entityColumns.sort(function (a, b) {
361
+ return a.rank - b.rank
362
+ })
363
+
364
+ // 컬럼 정보 txt 변환
365
+ let entityColsText: string[] = sortedCols.map(x => {
366
+ return this.columnToEntityColumn(x)
367
+ })
368
+
369
+ entityText = entityText.replace(/{{entityColumns}}/g, entityColsText.join('\n'))
370
+ return entityText
371
+ }
372
+
373
+ /**
374
+ * EntityColumn to entity column Text
375
+ * @param column
376
+ * @returns
377
+ */
378
+ columnToEntityColumn(column: EntityColumn) {
379
+ if (column.name == 'id') return ''
380
+ if (column.name == 'domain_id') return ''
381
+ if (column.name == 'created_at') return ''
382
+ if (column.name == 'updated_at') return ''
383
+ if (column.name == 'creator_id') return ''
384
+ if (column.name == 'updater_id') return ''
385
+
386
+ let colType: string = column.colType
387
+ if (column.colType == 'double') colType = 'double precision'
388
+ if (column.colType == 'long') colType = 'bigint'
389
+ if (column.colType == 'string') colType = 'character varying'
390
+ if (column.colType == 'datetime') colType = 'timestamp without time zone'
391
+
392
+ let colSize: string = ''
393
+ if (column.colType == 'string') {
394
+ //@ts-ignore
395
+ colSize = column.colSize
396
+ }
397
+
398
+ let fieldType: string = this.getFieldType(column)
399
+ let colTxtType: string = this.getColTxtType(column)
400
+
401
+ let colName = camelCase(column.name)
402
+
403
+ let columnAnn = `@Column({name:'${column.name}', type: '${colType}', nullable: ${column.nullable} ${colSize.length == 0 ? '' : ',length:' + colSize} })`
404
+ let fieldAnn = `@Field(${fieldType.length == 0 ? '' : 'type =>' + fieldType + ','} { nullable: ${column.nullable} })`
405
+ let columnTxt = `${colName}${column.nullable ? '?' : ''}: ${colTxtType}`
406
+
407
+ return ' ' + columnAnn + '\n ' + fieldAnn + '\n ' + columnTxt + '\n'
408
+ }
409
+
410
+ getFieldType(column: EntityColumn) {
411
+ let fieldType: string = ''
412
+
413
+ if (column.colType == 'double') fieldType = 'Float'
414
+ if (column.colType == 'float') fieldType = 'Float'
415
+ if (column.colType == 'long') fieldType = 'Int'
416
+ if (column.colType == 'integer') fieldType = 'Int'
417
+
418
+ return fieldType
419
+ }
420
+
421
+ getColTxtType(column: EntityColumn) {
422
+ let colTxtType: string = column.colType
423
+ if (column.colType == 'decimal') colTxtType = 'number'
424
+ if (column.colType == 'double') colTxtType = 'number'
425
+ if (column.colType == 'float') colTxtType = 'number'
426
+ if (column.colType == 'integer') colTxtType = 'number'
427
+ if (column.colType == 'long') colTxtType = 'number'
428
+ if (column.colType == 'datetime') colTxtType = 'Date'
429
+ if (column.colType == 'date') colTxtType = 'Date'
430
+ if (column.colType == 'text') colTxtType = 'string'
431
+ return colTxtType
432
+ }
433
+
434
+ /**
435
+ * 파일 생성
436
+ * @param filePath
437
+ * @param fileText
438
+ * @param nameMap
439
+ */
440
+ async writeFile(filePath: string, fileText: string, nameMap: any) {
441
+ await fs.writeFileSync(filePath, this.replaceNamesMap(fileText, nameMap), 'utf8')
442
+ }
443
+
444
+ /**
445
+ * 문자열 치환
446
+ * @param text
447
+ * @param nameMap
448
+ * @returns
449
+ */
450
+ replaceNamesMap(text: string, nameMap: any) {
451
+ return text
452
+ .replace(/{{pascalCase name}}/g, nameMap.pascalCaseName)
453
+ .replace(/{{camelCase name}}/g, nameMap.camelCaseName)
454
+ .replace(/{{snakeCase name}}/g, nameMap.snakeCaseName)
455
+ .replace(/{{pluralPascalCase name}}/g, nameMap.pluralPascalCaseName)
456
+ .replace(/{{pluralCamelCase name}}/g, nameMap.pluralCamelCaseName)
457
+ .replace(/{{tableName}}/g, nameMap.tableName)
458
+ .replace(/{{name}}/g, nameMap.name)
459
+ }
460
+
461
+ /**
462
+ * 서비스 경로 만들기
463
+ * @param appRootPath
464
+ * @param moduleName
465
+ * @param serviceName
466
+ */
467
+ async createServicePath(appRootPath: string, moduleName: string, serviceName: string) {
468
+ let path: string = appRootPath + '/' + 'packages'
469
+ await this.createDir(path)
470
+
471
+ path = path + '/' + moduleName
472
+ await this.createDir(path)
473
+
474
+ path = path + '/server'
475
+ await this.createDir(path)
476
+
477
+ path = path + '/service'
478
+ await this.createDir(path)
479
+
480
+ path = path + '/' + serviceName
481
+ await this.createDir(path)
482
+ }
483
+
484
+ /**
485
+ * 프로젝트 Root Path 구하기
486
+ * @returns String
487
+ */
488
+ getProjectRootPath() {
489
+ let appPath: string = process.env.PWD
490
+ let splitPath: string[] = appPath.split('/')
491
+ let pathArr: string[] = []
492
+ for (let i = 0; i < splitPath.length; i++) {
493
+ if (splitPath[i] == 'packages') {
494
+ break
495
+ }
496
+
497
+ pathArr.push(splitPath[i])
498
+ }
499
+
500
+ return pathArr.join('/')
501
+ }
502
+
503
+ /**
504
+ * 디렉토리 생성
505
+ * @param path
506
+ */
507
+ async createDir(path: string) {
508
+ if ((await this.existsPath(path)) == false) fs.mkdirSync(path)
509
+ }
510
+
511
+ /**
512
+ * Path 존재 여부
513
+ * @param path
514
+ * @returns
515
+ */
516
+ async existsPath(path: string) {
517
+ return await fs.existsSync(path)
518
+ }
519
+ }
520
+
521
+ /* all service index text index.ts */
522
+ const allIndex = `
523
+ /* EXPORT ENTITY TYPES */
524
+
525
+ /* IMPORT ENTITIES AND RESOLVERS */
526
+
527
+ export const entities = [
528
+ /* ENTITIES */
529
+ ]
530
+
531
+
532
+ export const schema = {
533
+ resolverClasses: [
534
+ /* RESOLVER CLASSES */
535
+ ]
536
+ }
537
+
538
+ export const subscribers = [
539
+ /* SUBSCRIBERS */
540
+ ]
541
+ `
542
+
543
+ /* service index text index.ts */
544
+ const serviceIndex = `
545
+ import { {{pascalCase name}} } from './{{name}}'
546
+ import { {{pascalCase name}}Query } from './{{name}}-query'
547
+ import { {{pascalCase name}}Mutation } from './{{name}}-mutation'
548
+
549
+ export const entities = [{{pascalCase name}}]
550
+ export const resolvers = [{{pascalCase name}}Query, {{pascalCase name}}Mutation]
551
+ `
552
+
553
+ /* service index text index.ts */
554
+ const serviceHistoryIndex = `
555
+ import { {{pascalCase name}} } from './{{name}}'
556
+ import { {{pascalCase name}}Query } from './{{name}}-query'
557
+ import { {{pascalCase name}}HistoryQuery } from './{{name}}-history-query'
558
+ import { {{pascalCase name}}Mutation } from './{{name}}-mutation'
559
+ import { {{pascalCase name}}History } from './{{name}}-history'
560
+ import { {{pascalCase name}}HistoryEntitySubscriber } from './event-subscriber'
561
+
562
+ export const entities = [{{pascalCase name}}, {{pascalCase name}}History]
563
+ export const resolvers = [{{pascalCase name}}Query, {{pascalCase name}}HistoryQuery, {{pascalCase name}}Mutation]
564
+ export const subscribers = [{{pascalCase name}}HistoryEntitySubscriber]
565
+
566
+ `
567
+
568
+ /* service Query text {{name}}-query.ts */
569
+ const serviceQuery = `
570
+ import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
571
+ import { Domain, ListParam, convertListParams, getRepository, getQueryBuilderFromListParams } from '@things-factory/shell'
572
+ import { User } from '@things-factory/auth-base'
573
+ import { {{pascalCase name}} } from './{{name}}'
574
+ import { {{pascalCase name}}List } from './{{name}}-type'
575
+
576
+ @Resolver({{pascalCase name}})
577
+ export class {{pascalCase name}}Query {
578
+ @Query(returns => {{pascalCase name}}, { description: 'To fetch a {{pascalCase name}}' })
579
+ async {{camelCase name}}(@Arg('id') id: string, @Ctx() context: any): Promise<{{pascalCase name}}> {
580
+ const { domain } = context.state
581
+ return await getRepository({{pascalCase name}}).findOne({
582
+ where: { domain: { id: domain.id }, id }
583
+ })
584
+ }
585
+
586
+ @Query(returns => {{pascalCase name}}List, { description: 'To fetch multiple {{pluralPascalCase name}}' })
587
+ async {{pluralCamelCase name}}(@Args(type => ListParam) params: ListParam, @Ctx() context: any): Promise<{{pascalCase name}}List> {
588
+ const { domain } = context.state
589
+
590
+ const queryBuilder = getQueryBuilderFromListParams({
591
+ domain,
592
+ params,
593
+ repository: await getRepository({{pascalCase name}})
594
+ })
595
+
596
+ const convertedParams = convertListParams(params, domain.id)
597
+ const [items, total] = await queryBuilder.getManyAndCount()
598
+
599
+ return { items, total }
600
+ }
601
+
602
+ @FieldResolver(type => Domain)
603
+ async domain(@Root() {{camelCase name}}: {{pascalCase name}}): Promise<Domain> {
604
+ return await getRepository(Domain).findOneBy({id:{{camelCase name}}.domainId})
605
+ }
606
+
607
+ @FieldResolver(type => User)
608
+ async updater(@Root() {{camelCase name}}: {{pascalCase name}}): Promise<User> {
609
+ return await getRepository(User).findOneBy({id:{{camelCase name}}.updaterId})
610
+ }
611
+
612
+ @FieldResolver(type => User)
613
+ async creator(@Root() {{camelCase name}}: {{pascalCase name}}): Promise<User> {
614
+ return await getRepository(User).findOneBy({id:{{camelCase name}}.creatorId})
615
+ }
616
+ }
617
+ `
618
+ /* service Query text {{name}}-query.ts */
619
+ const serviceHistoryQuery = `
620
+ import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
621
+ import { Domain, ListParam, convertListParams, getRepository, getQueryBuilderFromListParams } from '@things-factory/shell'
622
+ import { User } from '@things-factory/auth-base'
623
+ import { {{pascalCase name}}History } from './{{name}}-history'
624
+ import { {{pascalCase name}}HistoryList } from './{{name}}-history-type'
625
+
626
+ @Resolver({{pascalCase name}}History)
627
+ export class {{pascalCase name}}HistoryQuery {
628
+ @Query(returns => {{pascalCase name}}History, { description: 'To fetch a {{pascalCase name}}History' })
629
+ async {{camelCase name}}History(@Arg('id') id: string, @Ctx() context: any): Promise<{{pascalCase name}}History> {
630
+ const { domain } = context.state
631
+ return await getRepository({{pascalCase name}}History).findOne({
632
+ where: { domain: { id: domain.id }, id }
633
+ })
634
+ }
635
+
636
+ @Query(returns => {{pascalCase name}}HistoryList, { description: 'To fetch multiple {{pluralPascalCase name}}History' })
637
+ async {{camelCase name}}Histories(@Args(type => ListParam) params: ListParam, @Ctx() context: any): Promise<{{pascalCase name}}HistoryList> {
638
+ const { domain } = context.state
639
+
640
+ const queryBuilder = getQueryBuilderFromListParams({
641
+ domain,
642
+ params,
643
+ repository: await getRepository({{pascalCase name}}History)
644
+ })
645
+
646
+ const [items, total] = await queryBuilder.getManyAndCount()
647
+
648
+ return { items, total }
649
+ }
650
+
651
+ @FieldResolver(type => Domain)
652
+ async domain(@Root() {{camelCase name}}History: {{pascalCase name}}History): Promise<Domain> {
653
+ return await getRepository(Domain).findOneBy({id:{{camelCase name}}History.domainId})
654
+ }
655
+
656
+ @FieldResolver(type => User)
657
+ async updater(@Root() {{camelCase name}}History: {{pascalCase name}}History): Promise<User> {
658
+ return await getRepository(User).findOneBy({id:{{camelCase name}}History.updaterId})
659
+ }
660
+
661
+ @FieldResolver(type => User)
662
+ async creator(@Root() {{camelCase name}}History: {{pascalCase name}}History): Promise<User> {
663
+ return await getRepository(User).findOneBy({id:{{camelCase name}}History.creatorId})
664
+ }
665
+ }
666
+ `
667
+ /* service mutation text {{name}}-mutation.ts */
668
+ const serviceMutation = `
669
+ import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
670
+ import { In } from 'typeorm'
671
+ import { {{pascalCase name}} } from './{{name}}'
672
+ import { New{{pascalCase name}}, {{pascalCase name}}Patch } from './{{name}}-type'
673
+ import { getRepository } from '@things-factory/shell'
674
+
675
+ @Resolver({{pascalCase name}})
676
+ export class {{pascalCase name}}Mutation {
677
+ @Directive('@transaction')
678
+ @Mutation(returns => {{pascalCase name}}, { description: 'To create new {{pascalCase name}}' })
679
+ async create{{pascalCase name}}(@Arg('{{camelCase name}}') {{camelCase name}}: New{{pascalCase name}}, @Ctx() context: any): Promise<{{pascalCase name}}> {
680
+ const { domain, user, tx } = context.state
681
+
682
+ return await tx.getRepository({{pascalCase name}}).save({
683
+ ...{{camelCase name}},
684
+ domain,
685
+ creator: user,
686
+ updater: user
687
+ })
688
+ }
689
+
690
+ @Directive('@transaction')
691
+ @Mutation(returns => {{pascalCase name}}, { description: 'To modify {{pascalCase name}} information' })
692
+ async update{{pascalCase name}}(
693
+ @Arg('id') id: string,
694
+ @Arg('patch') patch: {{pascalCase name}}Patch,
695
+ @Ctx() context: any
696
+ ): Promise<{{pascalCase name}}> {
697
+ const { domain, user, tx } = context.state
698
+
699
+ const repository = tx.getRepository({{pascalCase name}})
700
+ const {{camelCase name}} = await repository.findOne(
701
+ {
702
+ where: { domain: { id: domain.id }, id },
703
+ relations: ['domain', 'updater', 'creator']
704
+ }
705
+ )
706
+
707
+ return await repository.save({
708
+ ...{{camelCase name}},
709
+ ...patch,
710
+ updater: user
711
+ })
712
+ }
713
+
714
+ @Directive('@transaction')
715
+ @Mutation(returns => [{{pascalCase name}}], { description: "To modify multiple {{pluralPascalCase name}}' information" })
716
+ async updateMultiple{{pascalCase name}}(
717
+ @Arg('patches', type => [{{pascalCase name}}Patch]) patches: {{pascalCase name}}Patch[],
718
+ @Ctx() context: any
719
+ ): Promise<{{pascalCase name}}[]> {
720
+ const { domain, user, tx } = context.state
721
+
722
+ let results = []
723
+ const _createRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === '+')
724
+ const _updateRecords = patches.filter((patch: any) => patch.cuFlag.toUpperCase() === 'M')
725
+ const {{camelCase name}}Repo = tx.getRepository({{pascalCase name}})
726
+
727
+ if (_createRecords.length > 0) {
728
+ for (let i = 0; i < _createRecords.length; i++) {
729
+ const newRecord = _createRecords[i]
730
+
731
+ const result = await {{camelCase name}}Repo.save({
732
+ ...newRecord,
733
+ domain,
734
+ creator: user,
735
+ updater: user
736
+ })
737
+
738
+ results.push({ ...result, cuFlag: '+' })
739
+ }
740
+ }
741
+
742
+ if (_updateRecords.length > 0) {
743
+ for (let i = 0; i < _updateRecords.length; i++) {
744
+ const updRecord = _updateRecords[i]
745
+ const {{camelCase name}} = await {{camelCase name}}Repo.findOne({
746
+ where: { domain: { id: domain.id }, id:updRecord.id },
747
+ relations: ['domain', 'updater', 'creator']
748
+ })
749
+
750
+ const result = await {{camelCase name}}Repo.save({
751
+ ...{{camelCase name}},
752
+ ...updRecord,
753
+ updater: user
754
+ })
755
+
756
+ results.push({ ...result, cuFlag: 'M' })
757
+ }
758
+ }
759
+
760
+ return results
761
+ }
762
+
763
+ @Directive('@transaction')
764
+ @Mutation(returns => Boolean, { description: 'To delete {{pascalCase name}}' })
765
+ async delete{{pascalCase name}}(@Arg('id') id: string, @Ctx() context: any): Promise<boolean> {
766
+ const { domain, tx, user } = context.state
767
+ await tx.getRepository({{pascalCase name}}).remove({ domain, id, updater:user })
768
+ return true
769
+ }
770
+
771
+ @Directive('@transaction')
772
+ @Mutation(returns => Boolean, { description: 'To delete multiple {{camelCase name}}s' })
773
+ async delete{{pluralPascalCase name}}(
774
+ @Arg('ids', type => [String]) ids: string[],
775
+ @Ctx() context: any
776
+ ): Promise<boolean> {
777
+ const { domain, tx, user } = context.state
778
+
779
+ let delEntitis = ids.map(id=>{
780
+ return {domain,id,updater:user}
781
+ })
782
+
783
+ await tx.getRepository({{pascalCase name}}).remove(delEntitis)
784
+
785
+ return true
786
+ }
787
+ }
788
+ `
789
+ /* service type text {{name}}-type.ts */
790
+ const sereviceType = `
791
+ import { ObjectType, Field, InputType, Int, ID, Float, registerEnumType } from 'type-graphql'
792
+
793
+ import { {{pascalCase name}} } from './{{name}}'
794
+
795
+ @InputType()
796
+ export class New{{pascalCase name}} {
797
+ {{newTypeColumns}}
798
+ }
799
+
800
+ @InputType()
801
+ export class {{pascalCase name}}Patch {
802
+ @Field(type => ID, { nullable: true })
803
+ id?: string
804
+
805
+ {{patchTypeColumns}}
806
+
807
+ @Field()
808
+ cuFlag: string
809
+ }
810
+
811
+ @ObjectType()
812
+ export class {{pascalCase name}}List {
813
+ @Field(type => [{{pascalCase name}}])
814
+ items: {{pascalCase name}}[]
815
+
816
+ @Field(type => Int)
817
+ total: number
818
+ }
819
+ `
820
+ /* service type text {{name}}-type.ts */
821
+ const sereviceHistoryType = `
822
+ import { ObjectType, Field, InputType, Int, ID, Float, registerEnumType } from 'type-graphql'
823
+
824
+ import { {{pascalCase name}}History } from './{{name}}-history'
825
+
826
+ @ObjectType()
827
+ export class {{pascalCase name}}HistoryList {
828
+ @Field(type => [{{pascalCase name}}History])
829
+ items: {{pascalCase name}}History[]
830
+
831
+ @Field(type => Int)
832
+ total: number
833
+ }
834
+ `
835
+
836
+ /* service entity {{name}}.ts */
837
+ const serviceEntity = `
838
+ import {
839
+ CreateDateColumn,
840
+ UpdateDateColumn,
841
+ Entity,
842
+ Index,
843
+ Column,
844
+ RelationId,
845
+ ManyToOne,
846
+ PrimaryGeneratedColumn,
847
+ VersionColumn
848
+ } from 'typeorm'
849
+ import { ObjectType, Field, Int, ID, Float, registerEnumType } from 'type-graphql'
850
+
851
+ import { Domain } from '@things-factory/shell'
852
+ import { User } from '@things-factory/auth-base'
853
+
854
+ @Entity('{{tableName}}')
855
+ {{indexAnn}}
856
+ @ObjectType({ description: 'Entity for {{pascalCase name}}' })
857
+ export class {{pascalCase name}} {
858
+ @PrimaryGeneratedColumn('uuid')
859
+ @Field(type => ID)
860
+ readonly id: string
861
+
862
+ @ManyToOne(type => Domain, {
863
+ createForeignKeyConstraints: false
864
+ })
865
+ @Field({ nullable: false })
866
+ domain: Domain
867
+
868
+ @RelationId(({{camelCase name}}: {{pascalCase name}}) => {{camelCase name}}.domain)
869
+ domainId: string
870
+
871
+ {{entityColumns}}
872
+
873
+ @CreateDateColumn()
874
+ @Field({ nullable: true })
875
+ createdAt?: Date
876
+
877
+ @UpdateDateColumn()
878
+ @Field({ nullable: true })
879
+ updatedAt?: Date
880
+
881
+ @ManyToOne(type => User, {
882
+ createForeignKeyConstraints: false,
883
+ nullable: true
884
+ })
885
+ @Field(type => User, { nullable: true })
886
+ creator?: User
887
+
888
+ @RelationId(({{camelCase name}}: {{pascalCase name}}) => {{camelCase name}}.creator)
889
+ creatorId?: string
890
+
891
+ @ManyToOne(type => User, {
892
+ createForeignKeyConstraints: false,
893
+ nullable: true
894
+ })
895
+ @Field(type => User, { nullable: true })
896
+ updater?: User
897
+
898
+ @RelationId(({{camelCase name}}: {{pascalCase name}}) => {{camelCase name}}.updater)
899
+ updaterId?: string
900
+ }
901
+ `
902
+
903
+ /* service entity {{name}}-history.ts */
904
+ const serviceEntityHistCopy = `
905
+ import {
906
+ CreateDateColumn,
907
+ UpdateDateColumn,
908
+ Entity,
909
+ Index,
910
+ Column,
911
+ RelationId,
912
+ ManyToOne,
913
+ PrimaryGeneratedColumn,
914
+ VersionColumn
915
+ } from 'typeorm'
916
+
917
+ import {
918
+ HistoryActionColumn,
919
+ HistoryActionType,
920
+ HistoryEntityInterface,
921
+ HistoryOriginalIdColumn
922
+ } from '@operato/typeorm-history'
923
+
924
+ import { ObjectType, Field, Int, ID, Float, registerEnumType } from 'type-graphql'
925
+
926
+ import { config } from '@things-factory/env'
927
+ import { Domain, ScalarObject } from '@things-factory/shell'
928
+ import { User } from '@things-factory/auth-base'
929
+
930
+ const ORMCONFIG = config.get('ormconfig', {})
931
+ const DATABASE_TYPE = ORMCONFIG.type
932
+
933
+ import { {{pascalCase name}} } from './{{name}}'
934
+
935
+ @Entity('{{tableName}}_histories')
936
+ @Index('ix_{{snakeCase name}}_histories_0', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.originalId,{{camelCase name}}History.dataRevisionNo], { unique: true })
937
+ @Index('ix_{{snakeCase name}}_histories_1', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.originalId ], { unique: false })
938
+ @Index('ix_{{snakeCase name}}_histories_2', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.domain, {{camelCase name}}History.originalId ], { unique: false })
939
+ @Index('ix_{{snakeCase name}}_histories_3', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.domain, {{camelCase name}}History.originalId, {{camelCase name}}History.dataRevisionNo], { unique: true })
940
+ @ObjectType({ description: 'Entity for {{pascalCase name}}History' })
941
+ export class {{pascalCase name}}History implements HistoryEntityInterface<{{pascalCase name}}>{
942
+ @PrimaryGeneratedColumn('uuid')
943
+ @Field(type => ID)
944
+ readonly id: string
945
+
946
+ @ManyToOne(type => Domain, {
947
+ createForeignKeyConstraints: false
948
+ })
949
+ @Field({ nullable: false })
950
+ domain: Domain
951
+
952
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.domain)
953
+ domainId: string
954
+
955
+ @HistoryOriginalIdColumn({type: 'character varying', nullable: false ,length:40})
956
+ @Field({ nullable: false })
957
+ public originalId!: string
958
+
959
+ @HistoryActionColumn({
960
+ nullable: false,
961
+ type:
962
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
963
+ ? 'enum'
964
+ : DATABASE_TYPE == 'oracle'
965
+ ? 'varchar2'
966
+ : DATABASE_TYPE == 'mssql'
967
+ ? 'nvarchar'
968
+ : 'varchar',
969
+ enum:
970
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
971
+ ? HistoryActionType
972
+ : undefined,
973
+ length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined: 32
974
+ })
975
+ @Field({ nullable: false })
976
+ public dataRevisionAction!: HistoryActionType
977
+
978
+ @Column({ default: 1, nullable: false })
979
+ @Field({ nullable: false })
980
+ dataRevisionNo?: number = 1
981
+
982
+ {{entityColumns}}
983
+
984
+ @CreateDateColumn()
985
+ @Field({ nullable: true })
986
+ createdAt?: Date
987
+
988
+ @UpdateDateColumn()
989
+ @Field({ nullable: true })
990
+ updatedAt?: Date
991
+
992
+ @ManyToOne(type => User, {
993
+ createForeignKeyConstraints: false,
994
+ nullable: true
995
+ })
996
+ @Field(type => User, { nullable: true })
997
+ creator?: User
998
+
999
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.creator)
1000
+ creatorId?: string
1001
+
1002
+ @ManyToOne(type => User, {
1003
+ createForeignKeyConstraints: false,
1004
+ nullable: true
1005
+ })
1006
+ @Field(type => User, { nullable: true })
1007
+ updater?: User
1008
+
1009
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.updater)
1010
+ updaterId?: string
1011
+ }
1012
+ `
1013
+
1014
+ /* service entity {{name}}-history.ts */
1015
+ const serviceEntityHistJson = `
1016
+ import {
1017
+ CreateDateColumn,
1018
+ UpdateDateColumn,
1019
+ Entity,
1020
+ Index,
1021
+ Column,
1022
+ RelationId,
1023
+ ManyToOne,
1024
+ PrimaryGeneratedColumn,
1025
+ VersionColumn
1026
+ } from 'typeorm'
1027
+
1028
+ import {
1029
+ HistoryActionColumn,
1030
+ HistoryActionType,
1031
+ HistoryEntityInterface,
1032
+ HistoryOriginalIdColumn
1033
+ } from '@operato/typeorm-history'
1034
+
1035
+ import { ObjectType, Field, Int, ID, Float, registerEnumType } from 'type-graphql'
1036
+
1037
+ import { config } from '@things-factory/env'
1038
+ import { Domain, ScalarObject } from '@things-factory/shell'
1039
+ import { User } from '@things-factory/auth-base'
1040
+
1041
+ const ORMCONFIG = config.get('ormconfig', {})
1042
+ const DATABASE_TYPE = ORMCONFIG.type
1043
+
1044
+ import { {{pascalCase name}} } from './{{name}}'
1045
+
1046
+ @Entity('{{tableName}}_histories')
1047
+ @Index('ix_{{snakeCase name}}_histories_0', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.originalId,{{camelCase name}}History.dataRevisionNo], { unique: true })
1048
+ @Index('ix_{{snakeCase name}}_histories_1', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.originalId ], { unique: false })
1049
+ @Index('ix_{{snakeCase name}}_histories_2', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.domain, {{camelCase name}}History.originalId ], { unique: false })
1050
+ @Index('ix_{{snakeCase name}}_histories_3', ({{camelCase name}}History: {{pascalCase name}}History) => [{{camelCase name}}History.domain, {{camelCase name}}History.originalId, {{camelCase name}}History.dataRevisionNo], { unique: true })
1051
+ @ObjectType({ description: 'Entity for {{pascalCase name}}History' })
1052
+ export class {{pascalCase name}}History implements HistoryEntityInterface<{{pascalCase name}}>{
1053
+ @PrimaryGeneratedColumn('uuid')
1054
+ @Field(type => ID)
1055
+ readonly id: string
1056
+
1057
+ @ManyToOne(type => Domain, {
1058
+ createForeignKeyConstraints: false
1059
+ })
1060
+ @Field({ nullable: false })
1061
+ domain: Domain
1062
+
1063
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.domain)
1064
+ domainId: string
1065
+
1066
+ @HistoryOriginalIdColumn({type: 'character varying', nullable: false ,length:40})
1067
+ @Field({ nullable: false })
1068
+ public originalId!: string
1069
+
1070
+ @HistoryActionColumn({
1071
+ nullable: false,
1072
+ type:
1073
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
1074
+ ? 'enum'
1075
+ : DATABASE_TYPE == 'oracle'
1076
+ ? 'varchar2'
1077
+ : DATABASE_TYPE == 'mssql'
1078
+ ? 'nvarchar'
1079
+ : 'varchar',
1080
+ enum:
1081
+ DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
1082
+ ? HistoryActionType
1083
+ : undefined,
1084
+ length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined: 32
1085
+ })
1086
+ @Field({ nullable: false })
1087
+ public dataRevisionAction!: HistoryActionType
1088
+
1089
+ @Column({ default: 1, nullable: false })
1090
+ @Field({ nullable: false })
1091
+ dataRevisionNo?: number = 1
1092
+
1093
+ @Column('simple-json', { nullable: true })
1094
+ @Field(type => ScalarObject, { nullable: true })
1095
+ historyJson?: any
1096
+
1097
+ @CreateDateColumn()
1098
+ @Field({ nullable: true })
1099
+ createdAt?: Date
1100
+
1101
+ @UpdateDateColumn()
1102
+ @Field({ nullable: true })
1103
+ updatedAt?: Date
1104
+
1105
+ @ManyToOne(type => User, {
1106
+ createForeignKeyConstraints: false,
1107
+ nullable: true
1108
+ })
1109
+ @Field(type => User, { nullable: true })
1110
+ creator?: User
1111
+
1112
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.creator)
1113
+ creatorId?: string
1114
+
1115
+ @ManyToOne(type => User, {
1116
+ createForeignKeyConstraints: false,
1117
+ nullable: true
1118
+ })
1119
+ @Field(type => User, { nullable: true })
1120
+ updater?: User
1121
+
1122
+ @RelationId(({{camelCase name}}History: {{pascalCase name}}History) => {{camelCase name}}History.updater)
1123
+ updaterId?: string
1124
+ }
1125
+ `
1126
+
1127
+ /* service entity subscriber event-subscriber.ts */
1128
+ const serviceEntitySubscriber = `
1129
+ import { EventSubscriber } from 'typeorm'
1130
+ import { HistoryEntitySubscriber } from '@operato/typeorm-history'
1131
+
1132
+ import { {{pascalCase name}} } from './{{name}}'
1133
+ import { {{pascalCase name}}History } from './{{name}}-history'
1134
+ import { getRepository } from '@things-factory/shell'
1135
+
1136
+ @EventSubscriber()
1137
+ export class {{pascalCase name}}HistoryEntitySubscriber extends HistoryEntitySubscriber<{{pascalCase name}},{{pascalCase name}}History> {
1138
+ public get entity() {
1139
+ return {{pascalCase name}}
1140
+ }
1141
+
1142
+ public get historyEntity() {
1143
+ return {{pascalCase name}}History
1144
+ }
1145
+
1146
+ {{entityToJson}}
1147
+
1148
+ async beforeRemoveHistory(history:{{pascalCase name}}History,entity:{{pascalCase name}}){
1149
+ let repo = getRepository({{pascalCase name}}History);
1150
+ const revNo = await repo.createQueryBuilder()
1151
+ .select('max(data_revision_no)', 'rev_no')
1152
+ .where('domain_id = :domainId', { domainId: entity.domain.id })
1153
+ .andWhere('original_id = :originalId', { originalId: entity.id })
1154
+ .getRawOne()
1155
+
1156
+ history.dataRevisionNo = revNo ? revNo.rev_no + 1 : 1;
1157
+ return history;
1158
+ }
1159
+ }
1160
+ `