@things-factory/operato-tools 8.0.6 → 9.0.0-beta.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,338 +0,0 @@
1
- import { Arg, Ctx, Mutation, Directive, Resolver } from 'type-graphql'
2
- import { Entity, EntityColumn } from '@things-factory/resource-base'
3
- import { Menu } from '@things-factory/menu-base'
4
- const { camelCase, startCase, snakeCase, kebabCase } = require('lodash')
5
- const { plural } = require('pluralize')
6
- import crypto from 'crypto'
7
-
8
- @Resolver()
9
- export class OperatoToolCreateMenu {
10
- @Directive('@transaction')
11
- @Mutation(returns => Boolean, { description: 'Operato Tool Create Menu' })
12
- async toolCreateMenu(@Arg('id') id: string, @Arg('parent_menu_id') parent_menu_id: string, @Ctx() context: any): Promise<Boolean> {
13
- const { domain, user, tx } = context.state
14
-
15
- // Entity 조회
16
- const entity: Entity = await tx.getRepository(Entity).findOne({
17
- where: {
18
- domain: { id: domain.id },
19
- id
20
- }
21
- })
22
-
23
- // Entity 컬럼 조회
24
- const entityColumns: EntityColumn[] = await tx.getRepository(EntityColumn).find({
25
- where: {
26
- domain: { id: domain.id },
27
- entity: { id: entity.id }
28
- }
29
- })
30
-
31
- let { name = '' } = entity || {}
32
- let serviceName: string = kebabCase(name)
33
-
34
- // 이름 치환 (타이틀 , grapql 관련 서비스 )
35
- let pageTemplate = modelTemplate
36
- .replace(/{{pascalCase name}}/g, startCase(camelCase(name)).replace(/ /g, ''))
37
- .replace(/{{camelCase name}}/g, camelCase(name))
38
- .replace(/{{snakeCase name}}/g, snakeCase(name))
39
- .replace(/{{pluralPascalCase name}}/g, startCase(camelCase(plural(name))).replace(/ /g, ''))
40
- .replace(/{{pluralCamelCase name}}/g, camelCase(plural(name)))
41
- .replace(/{{name}}/g, name)
42
-
43
- // 이력조회 버튼 추가
44
- let historyButton = ''
45
- if (entity.dataProp == 'JSON') {
46
- historyButton = `
47
- {
48
- "name": "history",
49
- "type": "basic",
50
- "label": "data_history",
51
- "icon": "history",
52
- "logic": "history_json"
53
- }
54
- `
55
- } else if (entity.dataProp == 'COPY') {
56
- historyButton = `
57
- {
58
- "name": "history",
59
- "type": "basic",
60
- "label": "data_history",
61
- "icon": "history",
62
- "logic": "history_copy"
63
- }
64
- `
65
- }
66
- pageTemplate = pageTemplate.replace(/{{HistoryButton}}/g, historyButton)
67
-
68
- // 그리드 정렬
69
- let sortColumns = this.createSortCols(entityColumns)
70
- pageTemplate = pageTemplate.replace(/{{SortColumns}}/g, sortColumns.join(','))
71
-
72
- // 검색 필드
73
- let searchColumns = this.createSearchCols(entityColumns)
74
- pageTemplate = pageTemplate.replace(/{{SearchColumns}}/g, searchColumns.join(','))
75
-
76
- // 그리드 컬럼
77
- let gridColumns = this.createGridColumns(entityColumns)
78
- pageTemplate = pageTemplate.replace(/{{GridColumns}}/g, gridColumns.join(','))
79
-
80
- // 암호화
81
- pageTemplate = this.enc(pageTemplate)
82
-
83
- // 메뉴 생성
84
- await this.createMenuData(tx, domain, user, parent_menu_id, serviceName, pageTemplate)
85
- return true
86
- }
87
-
88
- /**
89
- * 그리드 컬럼
90
- * @param entityColumns
91
- */
92
- createGridColumns(entityColumns: EntityColumn[]) {
93
- return entityColumns
94
- .filter(x => x.name != 'id' && x.gridRank > 0)
95
- .sort(function (a, b) {
96
- return a.gridRank - b.gridRank
97
- })
98
- .map(x => {
99
- let { gridEditor = 'string', name, term, gridWidth = 0, gridAlign = 'left', nullable = false } = x
100
- if (gridEditor == null) gridEditor = 'string'
101
- if (gridAlign == null) gridAlign = 'left'
102
-
103
- // 생성 자 수정 자 기본 값
104
- if (name == 'updater_id' || name == 'creator_id') {
105
- name = name == 'updater_id' ? 'updater' : 'creator'
106
- return `{"type": "object","name": "${name}","header": "${name}","width": 100,"editable":false, "align": "center","object_opt":{"queryName":"users"}}`
107
- }
108
- // 생성 시간 수정 시간 기본 값
109
- if (name == 'updated_at' || name == 'created_at') {
110
- return `{"type": "datetime","name": "${name == 'updated_at' ? 'updatedAt' : 'createdAt'}","header": "${name}","width": 160,"editable":false, "align": "center"}`
111
- }
112
-
113
- // 읽기 / 숨김 필드는 수정 불가
114
- let editable = gridEditor == 'readonly' || gridEditor == 'hidden' ? false : true
115
- // 히든 필드
116
- let hidden = gridEditor == 'hidden' ? true : false
117
- if (hidden == true || editable == false) {
118
- // 히든 필드는 문자열로 변환 숨김
119
- gridEditor = 'string'
120
- }
121
-
122
- let colTxt = `{"type": "${gridEditor}","name": "${camelCase(
123
- name
124
- )}","header": "${term}","hidden":${hidden} ,"editable": ${editable},"mandatory": ${!nullable},"sortable":true ,"align": "${gridAlign}","width":${gridWidth} ,"exportable": true`
125
-
126
- // 참조 타입이 공통 코드
127
- if (x.refType == 'code') {
128
- colTxt = colTxt + `,"select_opt":{"type":"code","name":"${x.refName}"}`
129
- }
130
-
131
- // 참조 타입이 시나리오
132
- if (x.refType == 'scenario') {
133
- colTxt = colTxt + `,"select_opt":{"type":"scenario","name":"${x.refName}"}`
134
- }
135
-
136
- // 참조 타입이 엔티티
137
- if (x.refType == 'entity') {
138
- colTxt = colTxt + `,"select_opt":{"type": "entity","args": {"queryName": "${camelCase(plural(x.refName))}","codeField": "id","dispField": "name"}}`
139
- }
140
- colTxt = colTxt + '}'
141
- return colTxt
142
- })
143
- }
144
-
145
- /**
146
- * 검색 컬럼
147
- * @param entityColumns
148
- */
149
- createSearchCols(entityColumns: EntityColumn[]) {
150
- return entityColumns
151
- .filter(x => x.searchRank > 0)
152
- .sort(function (a, b) {
153
- return a.searchRank - b.searchRank
154
- })
155
- .map(x => {
156
- let operator = x.searchOper
157
-
158
- if (operator == 'filter') {
159
- return `"${camelCase(x.name)}"`
160
- }
161
-
162
- if (!operator) operator = 'eq'
163
- return `{"name": "${camelCase(x.name)}","operator": "${operator}"}`
164
- })
165
- }
166
-
167
- /**
168
- * 그리드 정렬
169
- * @param entityColumns
170
- * @returns
171
- */
172
- createSortCols(entityColumns: EntityColumn[]) {
173
- return entityColumns
174
- .filter(x => x.sortRank > 0)
175
- .sort(function (a, b) {
176
- return a.sortRank - b.sortRank
177
- })
178
- .map(x => {
179
- return `{"name":"${camelCase(x.name)}","desc":${x.reverseSort}}`
180
- })
181
- }
182
-
183
- /**
184
- * 메뉴 데이터 생성
185
- * @param tx
186
- * @param domain
187
- * @param user
188
- * @param parent_menu_id
189
- * @param name
190
- * @param pageTemplate
191
- */
192
- async createMenuData(tx: any, domain: any, user: any, parent_menu_id: string, name: string, pageTemplate: string) {
193
- // 상위 메뉴
194
- let parentMenu = await tx.getRepository(Menu).findOne({
195
- where: {
196
- domain: { id: domain.id },
197
- id: parent_menu_id
198
- }
199
- })
200
-
201
- // 메뉴 랭크
202
- let menuRank = await tx.getRepository(Menu).query(`select coalesce(max(rank),0) as rank from menus where parent_id = '${parent_menu_id}' or id = '${parent_menu_id}'`)
203
- let rank = menuRank[0].rank + 10
204
-
205
- // 메뉴 저장
206
- let menu: Menu = new Menu()
207
- menu.name = name
208
- menu.menuType = 'SCREEN'
209
- menu.category = 'meta-grist-page'
210
- menu.rank = rank
211
- menu.hiddenFlag = false
212
- menu.routing = name
213
- menu.template = pageTemplate
214
- menu.resourceUrl = '@things-factory/meta-ui/client/pages/meta-grist-page'
215
- menu.parent = parentMenu
216
-
217
- await tx.getRepository(Menu).save({
218
- ...menu,
219
- domain,
220
- creator: user,
221
- updater: user
222
- })
223
- }
224
-
225
- /**
226
- * json 모델 암호화
227
- * @param template
228
- * @returns
229
- */
230
- enc(template: string) {
231
- let jsonStr = JSON.stringify(template)
232
- let templates = jsonStr.split(/(.{100})/).filter(O => O)
233
- let filters = ['aes-256-ecb', 'aes-256-cbc', 'aes-256-cfb', 'aes-256-cfb8', 'aes-256-cfb1', 'aes-256-ofb', 'aes-256-ctr', 'aes256']
234
- let salt = crypto.randomBytes(32).toString('hex')
235
- let hexKey = Buffer.from(salt, 'hex')
236
- let ivBase = salt.substring(0, 16)
237
- let iv = ['', ivBase, ivBase, ivBase, ivBase, ivBase, ivBase, ivBase]
238
- let filterIdx = 0
239
- let encResults = []
240
- encResults.push(salt)
241
-
242
- for (var i = 0; i < templates.length; i++, filterIdx++) {
243
- if (filterIdx == filters.length) filterIdx = 0
244
-
245
- let cipher = crypto.createCipheriv(filters[filterIdx], hexKey, iv[filterIdx])
246
- let encrypted = cipher.update(templates[i], 'utf8', 'hex')
247
- encrypted += cipher.final('hex')
248
- encResults.push(encrypted)
249
- }
250
-
251
- return encResults.join('h1z0q9i9x7q6')
252
- }
253
- }
254
-
255
- const modelTemplate = `
256
- {
257
- "menu": {
258
- "title": "{{name}}",
259
- "name": "",
260
- "desc": ""
261
- },
262
- "gql": {
263
- "query": {
264
- "list_func": "{{pluralCamelCase name}}",
265
- "find_one_func":"{{camelCase name}}"
266
- },
267
- "mutation":{
268
- "multiple":{
269
- "func":"updateMultiple{{pascalCase name}}",
270
- "type": "{{pascalCase name}}Patch"
271
- },
272
- "delete":{
273
- "func":"delete{{pluralPascalCase name}}"
274
- }
275
- }
276
- },
277
- "button": [
278
- {
279
- "name":"export"
280
- },
281
- {
282
- "name":"add"
283
- },
284
- {
285
- "name":"delete"
286
- },
287
- {
288
- "name":"save"
289
- }
290
- ],
291
- "grid_column": [
292
- {
293
- "type": "string",
294
- "name": "id",
295
- "header": "id",
296
- "hidden": true,
297
- "editable": false,
298
- "mandatory": false,
299
- "sortable": false,
300
- "align": "left",
301
- "width": 0,
302
- "exportable": false
303
- },
304
- {{GridColumns}}
305
- ],
306
- "grid": {
307
- "button":[
308
- {{HistoryButton}}
309
- ],
310
- "option": {
311
- "mobile_mode": "LIST",
312
- "desk_mode": "GRID",
313
- "use_row_checker": true,
314
- "pages": [
315
- 20,
316
- 50,
317
- 100,
318
- 300
319
- ],
320
- "view_mode": [
321
- "GRID",
322
- "LIST",
323
- "CARD"
324
- ],
325
- "sorters": [
326
- {{SortColumns}}
327
- ]
328
- },
329
- "row": {
330
- "multiple_select": true,
331
- "click": "select-row-toggle"
332
- }
333
- },
334
- "search": [
335
- {{SearchColumns}}
336
- ]
337
- }
338
- `