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