@things-factory/operato-tools 8.0.0 → 9.0.0-beta.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1160 +0,0 @@
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
- `