@bimatrix-aud-platform/aud_mcp_server 1.1.18 → 1.1.20
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.
- package/dist/generators/fixer.js +168 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/schemas/mtsd.interface.ts +7 -7
- package/schemas/mtsd.schema.json +6 -6
package/dist/generators/fixer.js
CHANGED
|
@@ -54,7 +54,11 @@ export function fixMtsd(filePath) {
|
|
|
54
54
|
fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes);
|
|
55
55
|
// Rule 2: OlapGrid DataSource 기반 Fields 자동 생성
|
|
56
56
|
fixOlapGridFields(doc, datas, fixes);
|
|
57
|
-
// Rule
|
|
57
|
+
// Rule 2-2: OlapGrid Fields 내 CreateType=1 (Measures) 필드 보정
|
|
58
|
+
fixOlapMeasuresField(doc, fixes);
|
|
59
|
+
// Rule 3: Enum/Range 값 범위 초과 보정
|
|
60
|
+
fixEnumAndRangeValues(doc, datas, fixes);
|
|
61
|
+
// Rule (disabled): DataSource Params에 Value 누락 보정
|
|
58
62
|
//fixParamsMissingValue(datas, fixes);
|
|
59
63
|
// Rule 4: DataSource Columns에 Type 누락 보정
|
|
60
64
|
//fixColumnsMissingType(datas, fixes);
|
|
@@ -193,6 +197,38 @@ function fixOlapGridFields(doc, datas, fixes) {
|
|
|
193
197
|
});
|
|
194
198
|
}
|
|
195
199
|
}
|
|
200
|
+
// ---- Rule 2-2: OlapGrid Fields 내 CreateType=1 (Measures) 필드 보정 ----
|
|
201
|
+
// CreateType=1은 Key="#MEASURES_HEADER#"인 항목만 가질 수 있다.
|
|
202
|
+
// CreateType=1이 2개 이상이면, Key가 "#MEASURES_HEADER#"가 아닌 필드의 CreateType을 0으로 변경한다.
|
|
203
|
+
function fixOlapMeasuresField(doc, fixes) {
|
|
204
|
+
const forms = doc.Forms || [];
|
|
205
|
+
for (const form of forms) {
|
|
206
|
+
const elements = form.Elements || [];
|
|
207
|
+
walkElements(elements, (el, path) => {
|
|
208
|
+
if (el.Type !== "OlapGrid")
|
|
209
|
+
return;
|
|
210
|
+
const fields = el.iOLAPView?.Fields;
|
|
211
|
+
if (!fields || !Array.isArray(fields) || fields.length === 0)
|
|
212
|
+
return;
|
|
213
|
+
// CreateType=1인 필드 수집
|
|
214
|
+
const createType1Fields = [];
|
|
215
|
+
for (const field of fields) {
|
|
216
|
+
if (field.CreateType === 1) {
|
|
217
|
+
createType1Fields.push(field);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// 2개 이상일 때만 보정
|
|
221
|
+
if (createType1Fields.length < 2)
|
|
222
|
+
return;
|
|
223
|
+
for (const field of createType1Fields) {
|
|
224
|
+
if (field.Key !== "#MEASURES_HEADER#") {
|
|
225
|
+
field.CreateType = 0; // enOlapFieldCreateType.Default
|
|
226
|
+
fixes.push(`[Rule2-2] ${path}.Fields["${field.Key}"]: CreateType 1→0 보정 (#MEASURES_HEADER#만 CreateType=1 가능)`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
196
232
|
// ---- Rule 3: Params Value 누락 보정 ----
|
|
197
233
|
// function fixParamsMissingValue(datas: any[], fixes: string[]) {
|
|
198
234
|
// for (const ds of datas) {
|
|
@@ -274,6 +310,137 @@ const BORDER_DEFAULTS = {
|
|
|
274
310
|
// });
|
|
275
311
|
// }
|
|
276
312
|
// }
|
|
313
|
+
// ---- Rule 3: Enum/Range 값 범위 초과 보정 ----
|
|
314
|
+
// string enum 검증: 허용값에 없으면 기본값으로 보정
|
|
315
|
+
function fixStringEnum(obj, key, allowed, defaultVal, path, fixes) {
|
|
316
|
+
if (!(key in obj))
|
|
317
|
+
return;
|
|
318
|
+
const val = obj[key];
|
|
319
|
+
if (typeof val !== "string" || !allowed.includes(val)) {
|
|
320
|
+
obj[key] = defaultVal;
|
|
321
|
+
fixes.push(`[Rule3] ${path}.${key}: "${val}" → "${defaultVal}" 보정 (허용값: ${allowed.join(", ")})`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// integer 범위 검증: min~max 범위 밖이면 기본값으로 보정
|
|
325
|
+
function fixIntRange(obj, key, min, max, defaultVal, path, fixes) {
|
|
326
|
+
if (!(key in obj))
|
|
327
|
+
return;
|
|
328
|
+
const val = obj[key];
|
|
329
|
+
if (typeof val !== "number" || !Number.isInteger(val) || val < min || val > max) {
|
|
330
|
+
obj[key] = defaultVal;
|
|
331
|
+
fixes.push(`[Rule3] ${path}.${key}: ${val} → ${defaultVal} 보정 (범위: ${min}~${max})`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// integer enum 검증: 허용 값 집합에 없으면 기본값으로 보정 (비연속 enum용)
|
|
335
|
+
function fixIntEnum(obj, key, allowed, defaultVal, path, fixes) {
|
|
336
|
+
if (!(key in obj))
|
|
337
|
+
return;
|
|
338
|
+
const val = obj[key];
|
|
339
|
+
if (typeof val !== "number" || !allowed.includes(val)) {
|
|
340
|
+
obj[key] = defaultVal;
|
|
341
|
+
fixes.push(`[Rule3] ${path}.${key}: ${val} → ${defaultVal} 보정 (허용값: ${allowed.join(", ")})`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// RGBA 색상 clamp (0~255)
|
|
345
|
+
function clampColorRGBA(obj, path, fixes) {
|
|
346
|
+
if (!obj || typeof obj !== "object")
|
|
347
|
+
return;
|
|
348
|
+
for (const key of ["R", "G", "B", "A", "ColorR", "ColorG", "ColorB", "ColorA"]) {
|
|
349
|
+
if (!(key in obj))
|
|
350
|
+
continue;
|
|
351
|
+
const val = obj[key];
|
|
352
|
+
if (typeof val !== "number")
|
|
353
|
+
continue;
|
|
354
|
+
if (val < 0) {
|
|
355
|
+
obj[key] = 0;
|
|
356
|
+
fixes.push(`[Rule3] ${path}.${key}: ${val} → 0 보정 (범위: 0~255)`);
|
|
357
|
+
}
|
|
358
|
+
else if (val > 255) {
|
|
359
|
+
obj[key] = 255;
|
|
360
|
+
fixes.push(`[Rule3] ${path}.${key}: ${val} → 255 보정 (범위: 0~255)`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function fixEnumAndRangeValues(doc, datas, fixes) {
|
|
365
|
+
// ── 1. ReportInfo ──
|
|
366
|
+
const ri = doc.ReportInfo;
|
|
367
|
+
if (ri) {
|
|
368
|
+
fixIntRange(ri, "TabPosition", 0, 2, 0, "ReportInfo", fixes); // 기본값: 1(None)
|
|
369
|
+
fixIntRange(ri, "RefreshType", 0, 1, 0, "ReportInfo", fixes); // 기본값: 0(All)
|
|
370
|
+
}
|
|
371
|
+
// ── 2. DataSources ──
|
|
372
|
+
for (const ds of datas) {
|
|
373
|
+
const dsPath = `DataSource("${ds.Name || ds.Id || "?"}")`;
|
|
374
|
+
fixIntRange(ds, "DSType", 0, 5, 2, dsPath, fixes); // 기본값: 2(DataSource)
|
|
375
|
+
}
|
|
376
|
+
// ── 3. FormStyle ──
|
|
377
|
+
const forms = doc.Forms || [];
|
|
378
|
+
for (const form of forms) {
|
|
379
|
+
if (form.Style) {
|
|
380
|
+
fixIntRange(form.Style, "Type", 0, 2, 0, `Form("${form.Name}").Style`, fixes); // 기본값: 0(Skin)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// ── 4. Elements (walkElements 순회) ──
|
|
384
|
+
for (const form of forms) {
|
|
385
|
+
const elements = form.Elements || [];
|
|
386
|
+
walkElements(elements, (el, path) => {
|
|
387
|
+
// ---- Style.Border.LineType ----
|
|
388
|
+
const border = el.Style?.Border;
|
|
389
|
+
if (border) {
|
|
390
|
+
fixStringEnum(border, "LineType", ["none", "solid", "dashed", "dotted"], "solid", // 기본값: "solid"
|
|
391
|
+
`${path}.Style.Border`, fixes);
|
|
392
|
+
clampColorRGBA(border, `${path}.Style.Border`, fixes);
|
|
393
|
+
if (border.Color)
|
|
394
|
+
clampColorRGBA(border.Color, `${path}.Style.Border.Color`, fixes);
|
|
395
|
+
}
|
|
396
|
+
// ---- Style.Font.HorizontalAlignment / VerticalAlignment ----
|
|
397
|
+
const font = el.Style?.Font;
|
|
398
|
+
if (font) {
|
|
399
|
+
fixStringEnum(font, "HorizontalAlignment", ["left", "center", "right"], "left", // 기본값: "left"
|
|
400
|
+
`${path}.Style.Font`, fixes);
|
|
401
|
+
fixStringEnum(font, "VerticalAlignment", ["top", "middle", "bottom"], "middle", // 기본값: "middle"
|
|
402
|
+
`${path}.Style.Font`, fixes);
|
|
403
|
+
if (font.Color)
|
|
404
|
+
clampColorRGBA(font.Color, `${path}.Style.Font.Color`, fixes);
|
|
405
|
+
}
|
|
406
|
+
// ---- Style.Background Color ----
|
|
407
|
+
const bg = el.Style?.Background;
|
|
408
|
+
if (bg) {
|
|
409
|
+
clampColorRGBA(bg, `${path}.Style.Background`, fixes);
|
|
410
|
+
if (bg.Color)
|
|
411
|
+
clampColorRGBA(bg.Color, `${path}.Style.Background.Color`, fixes);
|
|
412
|
+
}
|
|
413
|
+
// ---- DataGrid / TreeGrid Columns ----
|
|
414
|
+
if ((el.Type === "DataGrid" || el.Type === "TreeGrid") && Array.isArray(el.Columns)) {
|
|
415
|
+
for (let i = 0; i < el.Columns.length; i++) {
|
|
416
|
+
const col = el.Columns[i];
|
|
417
|
+
const colPath = `${path}.Columns[${i}]("${col.Name || ""}")`;
|
|
418
|
+
fixStringEnum(col, "HeaderPosition", ["left", "center", "right", "start", "end"], "center", // 기본값: "center"
|
|
419
|
+
colPath, fixes);
|
|
420
|
+
fixStringEnum(col, "TextPosition", ["left", "center", "right", "start", "end"], "left", // 기본값: "left"
|
|
421
|
+
colPath, fixes);
|
|
422
|
+
// 0:None, 1:Text, 2:CheckBox, 3:NumberBox, 4:ComboBox, 5:DateTime, 6:MaskEdit, 8:Image, 9:MultiLineText, 10:TrendLine, 11:SingleBarChart, 12:ColorPicker, 15:Time
|
|
423
|
+
fixIntEnum(col, "ColumnType", [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 15], 1, colPath, fixes); // 기본값: 0(None)
|
|
424
|
+
fixIntRange(col, "KeyType", 0, 3, 2, colPath, fixes); // 기본값: 2(Nullable)
|
|
425
|
+
fixIntRange(col, "DataType", 0, 2, 0, colPath, fixes); // 기본값: 1(String)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// ---- OlapGrid Fields ----
|
|
429
|
+
if (el.Type === "OlapGrid") {
|
|
430
|
+
const fields = el.iOLAPView?.Fields;
|
|
431
|
+
if (fields && Array.isArray(fields)) {
|
|
432
|
+
for (const field of fields) {
|
|
433
|
+
const fPath = `${path}.Fields["${field.Key || "?"}"]`;
|
|
434
|
+
fixIntRange(field, "Category", 0, 4, 1, fPath, fixes); // 기본값: 1(Dimension)
|
|
435
|
+
fixIntRange(field, "Area", 0, 4, 3, fPath, fixes); // 기본값: 3(Filter)
|
|
436
|
+
fixIntRange(field, "SortType", 0, 5, 0, fPath, fixes); // 기본값: 0(None)
|
|
437
|
+
fixIntRange(field, "CreateType", 0, 3, 0, fPath, fixes); // 기본값: 0(Default)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
277
444
|
// ---- 유틸: Element 트리 순회 ----
|
|
278
445
|
function walkElements(elements, callback, parentPath = "") {
|
|
279
446
|
for (const el of elements) {
|
package/dist/index.js
CHANGED
|
@@ -545,7 +545,7 @@ const tools = [
|
|
|
545
545
|
},
|
|
546
546
|
{
|
|
547
547
|
name: "fix_mtsd",
|
|
548
|
-
description: "MTSD 파일을 읽어 자동 보정 규칙을 적용하고 파일을 덮어씁니다. DataSource Name→Id 참조 보정,
|
|
548
|
+
description: "MTSD 파일을 읽어 자동 보정 규칙을 적용하고 파일을 덮어씁니다. [Rule1] DataSource Name→Id 참조 보정, [Rule2] OlapGrid DataSource 기반 Fields 자동 생성, [Rule2-2] OlapGrid Fields 내 CreateType=1 중복 보정 (#MEASURES_HEADER#만 허용), [Rule3] Enum/Range 값 범위 초과 보정 (Border.LineType, Font 정렬, Color RGBA clamp, GridColumn 속성, OlapField 속성 등), Params ParamType 누락 보정을 수행합니다.",
|
|
549
549
|
inputSchema: {
|
|
550
550
|
type: "object",
|
|
551
551
|
properties: {
|
package/package.json
CHANGED
|
@@ -115,7 +115,7 @@ export interface IDataSource {
|
|
|
115
115
|
ConnectionCode: string;
|
|
116
116
|
/** SQL 쿼리 암호화 여부 */
|
|
117
117
|
Encrypted: boolean;
|
|
118
|
-
/** 데이터소스 타입 (0
|
|
118
|
+
/** 데이터소스 타입 (0:메타 템플릿,1:메타 쿼리, 2:일반 SQL,3:OLAP Drill to Detail,4:공통데이터소스,5:메타 필터) */
|
|
119
119
|
DSType: number;
|
|
120
120
|
/** 실행할 SQL 쿼리문 */
|
|
121
121
|
SQL: string;
|
|
@@ -657,13 +657,13 @@ export interface IGridColumn {
|
|
|
657
657
|
Resizable?: boolean;
|
|
658
658
|
/** 내보내기 포함 여부 */
|
|
659
659
|
UseExport?: boolean;
|
|
660
|
-
/** 컬럼 타입 (0: Text,
|
|
660
|
+
/** 컬럼 타입 (0:None, 1:Text, 2:CheckBox, 3:NumberBox, 4:ComboBox, 5:DateTime, 6:MaskEdit, 8:Image, 9:MultiLineText, 10:TrendLine, 11:SingleBarChart, 12:ColorPicker, 15:Time) */
|
|
661
661
|
ColumnType?: number;
|
|
662
662
|
/** 커서 타입 */
|
|
663
663
|
CursorType?: string;
|
|
664
|
-
/** 키 타입 (0:
|
|
664
|
+
/** 키 타입 (0:None, 1:NotNull, 2:Nullable, 3:Primary) */
|
|
665
665
|
KeyType: number;
|
|
666
|
-
/** 데이터 타입 (0: String,
|
|
666
|
+
/** 데이터 타입 (0:Numeric, 1:String, 2:DateTime8(yyyyMMdd), 3:DateTimeNow, 4:UserCode, 5:CLOB, 7:UUID) */
|
|
667
667
|
DataType?: number;
|
|
668
668
|
/** 헤더 텍스트 정렬 */
|
|
669
669
|
HeaderPosition: 'left' | 'center' | 'right' | 'start' | 'end';
|
|
@@ -1425,7 +1425,7 @@ export interface IOlapTopFilter {
|
|
|
1425
1425
|
* @see src/control/olapgrid/iOLAP.Model.ts - OlapField.Serialize()
|
|
1426
1426
|
*/
|
|
1427
1427
|
export interface IOlapField {
|
|
1428
|
-
/** 필드 키 */
|
|
1428
|
+
/** 필드 키 (데이터 소스의 컬럼명, 계산 필드일 경우 동적으로 생성)*/
|
|
1429
1429
|
Key: string;
|
|
1430
1430
|
/** 필드 캡션 */
|
|
1431
1431
|
Caption: string;
|
|
@@ -1433,7 +1433,7 @@ export interface IOlapField {
|
|
|
1433
1433
|
ToolTipField?: string;
|
|
1434
1434
|
/** 툴팁 텍스트 */
|
|
1435
1435
|
ToolTipText?: string;
|
|
1436
|
-
/** 필드 카테고리 (0:
|
|
1436
|
+
/** 필드 카테고리 (0: Default, 1: Dimension, 2: Measure, 3: Attribute, 4: Period) */
|
|
1437
1437
|
Category: number;
|
|
1438
1438
|
/** 필드 영역 (0: None, 1: Row, 2: Column, 3: Data, 4: Filter) */
|
|
1439
1439
|
Area: number;
|
|
@@ -1457,7 +1457,7 @@ export interface IOlapField {
|
|
|
1457
1457
|
Width?: number;
|
|
1458
1458
|
/** 단위 */
|
|
1459
1459
|
Unit?: string;
|
|
1460
|
-
/** 생성 유형 (0:
|
|
1460
|
+
/** 생성 유형 (0:Default, 1:Measures(특수필드로 다른 Measure필드의 집합, Files내 1개만 자동 생성됨), 2:DimensionGroup, 3:HierarchyGroup) */
|
|
1461
1461
|
CreateType?: number;
|
|
1462
1462
|
/** 정렬 유형 (0: None, 1: Asc, 2: Desc) */
|
|
1463
1463
|
SortType?: number;
|
package/schemas/mtsd.schema.json
CHANGED
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"UseCache": { "type": "boolean", "description": "쿼리 결과 캐싱 사용 여부" },
|
|
103
103
|
"ConnectionCode": { "type": "string", "description": "연결된 DB 커넥션 코드" },
|
|
104
104
|
"Encrypted": { "type": "boolean", "description": "SQL 쿼리 암호화 여부" },
|
|
105
|
-
"DSType": { "type": "integer", "enum": [0, 1, 2, 3], "description": "데이터소스 타입 (0
|
|
105
|
+
"DSType": { "type": "integer", "enum": [0, 1, 2, 3,4,5], "description": "데이터소스 타입 (0:메타 템플릿,1:메타 쿼리, 2:일반 SQL,3:OLAP Drill to Detail,4:공통데이터소스,5:메타 필터)" },
|
|
106
106
|
"SQL": { "type": "string", "description": "실행할 SQL 쿼리문" },
|
|
107
107
|
"Params": {
|
|
108
108
|
"type": "array",
|
|
@@ -1019,7 +1019,7 @@
|
|
|
1019
1019
|
"Caption": { "type": "string", "description": "필드 캡션" },
|
|
1020
1020
|
"ToolTipField": { "type": "string", "description": "툴팁 필드 키" },
|
|
1021
1021
|
"ToolTipText": { "type": "string", "description": "툴팁 텍스트" },
|
|
1022
|
-
"Category": { "type": "number", "description": "카테고리 (0:
|
|
1022
|
+
"Category": { "type": "number", "description": "카테고리 (0: Default, 1: Dimension, 2: Measure, 3: Attribute, 4: Period)" },
|
|
1023
1023
|
"Area": { "type": "number", "description": "영역 (0:None, 1:Row, 2:Column, 3:Data, 4:Filter)" },
|
|
1024
1024
|
"SummaryType": { "type": "number", "description": "요약 유형 (0:Sum, 1:Count, 2:Average, 3:Min, 4:Max 등)" },
|
|
1025
1025
|
"TotalSummaryType": { "type": "number", "description": "합계 요약 유형" },
|
|
@@ -1031,7 +1031,7 @@
|
|
|
1031
1031
|
"RefFormula": { "type": "string", "description": "참조 수식" },
|
|
1032
1032
|
"Width": { "type": "number", "description": "필드 너비" },
|
|
1033
1033
|
"Unit": { "type": "string", "description": "단위" },
|
|
1034
|
-
"CreateType": { "type": "number", "description": "생성 유형 (0:Default, 1:
|
|
1034
|
+
"CreateType": { "type": "number", "description": "생성 유형 (0:Default, 1:Measures(특수필드로 다른 Measure필드의 집합, Files내 1개만 자동 생성됨), 2:DimensionGroup, 3:HierarchyGroup)" },
|
|
1035
1035
|
"SortType": { "type": "number", "description": "정렬 유형 (0:None, 1:Asc, 2:Desc)" },
|
|
1036
1036
|
"MeasureSortField": { "type": "string", "description": "측정값 정렬 기준 필드" },
|
|
1037
1037
|
"SortBaseField": { "type": "string", "description": "정렬 기준 필드" },
|
|
@@ -1325,10 +1325,10 @@
|
|
|
1325
1325
|
"Mergeable": { "type": "boolean" },
|
|
1326
1326
|
"Resizable": { "type": "boolean" },
|
|
1327
1327
|
"UseExport": { "type": "boolean" },
|
|
1328
|
-
"ColumnType": { "type": "integer", "description": "컬럼 타입 (0:
|
|
1328
|
+
"ColumnType": { "type": "integer", "description": "컬럼 타입 (0:None, 1:Text, 2:CheckBox, 3:NumberBox, 4:ComboBox, 5:DateTime, 6:MaskEdit, 8:Image, 9:MultiLineText, 10:TrendLine, 11:SingleBarChart, 12:ColorPicker, 15:Time)" },
|
|
1329
1329
|
"CursorType": { "type": "string" },
|
|
1330
|
-
"KeyType": { "type": "integer", "description": "키 타입 (0:None, 1:
|
|
1331
|
-
"DataType": { "type": "integer", "description": "데이터 타입 (0:
|
|
1330
|
+
"KeyType": { "type": "integer", "description": "키 타입 (0:None, 1:NotNull, 2:Nullable, 3:Primary)" },
|
|
1331
|
+
"DataType": { "type": "integer", "description": "데이터 타입 (0:Numeric, 1:String, 2:DateTime8(yyyyMMdd), 3:DateTimeNow, 4:UserCode, 5:CLOB, 7:UUID)" },
|
|
1332
1332
|
"HeaderPosition": { "type": "string", "enum": ["left", "center", "right", "start", "end"] },
|
|
1333
1333
|
"TextPosition": { "type": "string", "enum": ["left", "center", "right", "start", "end"] },
|
|
1334
1334
|
"GridColumnWidthType": { "type": "integer" },
|