fix(scaffold): apply target system code naming for MCI converter and abbreviate baseName
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 17s

This commit is contained in:
jade
2026-09-10 14:35:42 +09:00
parent b20a4edfdd
commit 115e403497
3 changed files with 113 additions and 37 deletions

View File

@@ -2762,7 +2762,7 @@ function initializeMciResponseNames(parsed) {
const labels = mciTransformLabels();
const baseName = rootName.replace(/_[OI]$/, '') || 'MciOutput';
const dtoName = `${baseName}${labels.dto}`;
const converterName = `${baseName}${labels.dto}Converter`;
const converterName = `${baseName}Converter`;
document.getElementById('mciResponseClassName').value = dtoName;
document.getElementById('mciConverterClassName').value = converterName;
mciResponseState.autoConverterName = converterName;
@@ -2999,9 +2999,14 @@ function downloadMciResponseSource(kind) {
}
function converterNameFor(responseName) {
return responseName && responseName.endsWith('Response')
? responseName.slice(0, -8) + 'Converter'
: (responseName ? responseName + 'Converter' : '');
if (!responseName) return '';
if (responseName.endsWith('Response')) {
return responseName.slice(0, -8) + 'Converter';
}
if (responseName.endsWith('Request')) {
return responseName.slice(0, -7) + 'Converter';
}
return responseName + 'Converter';
}
function setMciResponseButtonBusy(button, busy, text) {

View File

@@ -94,7 +94,11 @@ public class ToolScaffolder {
if (tools == null || tools.isEmpty()) {
throw new IllegalArgumentException("At least one Tool method is required.");
}
boolean hasMci = tools.stream().anyMatch(t -> "MCI".equalsIgnoreCase(t.routingType()));
String useCaseBaseName = toPascalCase(useCaseName);
if (hasMci) {
useCaseBaseName = abbreviatedMciSourceBaseName(useCaseBaseName);
}
String group = tools.getFirst().group().toLowerCase(Locale.ROOT);
validateToolMethods(tools, group);
@@ -128,7 +132,9 @@ public class ToolScaffolder {
} else {
writeUtf8(useCaseFile, groupedUseCaseContent(bizPackage, useCaseBaseName, moduleName, tools));
writeUtf8(useCaseImplFile, groupedUseCaseImplContent(bizPackage, useCaseBaseName, tools));
writeUtf8(converterFile, groupedConverterContent(bizPackage, useCaseBaseName, tools));
if (!hasMci) {
writeUtf8(converterFile, groupedConverterContent(bizPackage, useCaseBaseName, tools));
}
}
StringBuilder log = new StringBuilder("\n=========================================\n")
@@ -145,7 +151,9 @@ public class ToolScaffolder {
log.append("[HTTP Config] ").append(glowConfig).append("\n");
}
}
log.append("[Converter] ").append(converterFile).append("\n");
if (!hasMci) {
log.append("[Converter] ").append(converterFile).append("\n");
}
return log.toString();
}
@@ -206,8 +214,9 @@ public class ToolScaffolder {
private static void writeGroupedToolFiles(Path moduleRoot, Path sourceRoot, Path dtoDir, Path definitionDir,
String bizPackage, ToolMethodDefinition tool,
String moduleName, StringBuilder log) throws IOException {
String baseName = toPascalCase(tool.baseName());
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String toolBaseName = toPascalCase(tool.baseName());
String baseName = mci ? abbreviatedMciSourceBaseName(toolBaseName) : toolBaseName;
String code = mci ? formatClientSystemCode(tool.clientSystemCode(), "/") : toPackageSegment(tool.httpApiName());
String ioPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." : ".infra.itrf.http.") + code.replace("/", ".");
Path clientDir = sourceRoot.resolve(Paths.get("infra", "itrf", mci ? "mci" : "http", code));
@@ -258,8 +267,20 @@ public class ToolScaffolder {
} else {
writeUtf8(clientDir.resolve(baseName + "Client.java"), groupedMciClientContent(ioPackage, baseName, tool.interfaceId()));
}
writeUtf8(sourceRoot.resolve(Paths.get("biz", tool.group().toLowerCase(Locale.ROOT), "converter", baseName + "Converter.java")),
groupedMciConverterContent(bizPackage, baseName, ioPackage, ioPrefix));
String targetPkg = mciTargetSystemPackage(tool.clientSystemCode());
String converterPkg = bizPackage + ".converter" + (targetPkg != null ? "." + targetPkg : "");
Path targetConverterDir = sourceRoot.resolve(Paths.get("biz", tool.group().toLowerCase(Locale.ROOT), "converter"));
if (targetPkg != null) {
for (String seg : targetPkg.split("\\.")) {
targetConverterDir = targetConverterDir.resolve(seg);
}
}
Files.createDirectories(targetConverterDir);
String converterName = (tool.clientSystemCode() != null && !tool.clientSystemCode().isBlank())
? tool.clientSystemCode().toUpperCase(Locale.ROOT) + "Converter"
: baseName + "Converter";
writeUtf8(targetConverterDir.resolve(converterName + ".java"),
groupedMciConverterContent(converterPkg, bizPackage, baseName, ioPackage, ioPrefix, converterName));
} else {
writeUtf8(ioDir.resolve(baseName + "HttpRequest.java"),
dtoContent(ioPackage + ".io", baseName + "HttpRequest", tool.inputFields(), "", "", true));
@@ -273,7 +294,7 @@ public class ToolScaffolder {
groupedHttpConverterContent(bizPackage, baseName, ioPackage));
}
String toolName = toToolName(moduleName, tool.group(), baseName);
String toolName = toToolName(moduleName, tool.group(), toolBaseName);
log.append("[Tool] ").append(toolName).append(" -> ").append(clientDir.resolve(baseName + "Client.java")).append("\n");
}
@@ -282,13 +303,15 @@ public class ToolScaffolder {
StringBuilder imports = new StringBuilder();
StringBuilder methods = new StringBuilder();
for (ToolMethodDefinition tool : tools) {
String baseName = toPascalCase(tool.baseName());
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String toolBaseName = toPascalCase(tool.baseName());
String baseName = mci ? abbreviatedMciSourceBaseName(toolBaseName) : toolBaseName;
imports.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Request;\n")
.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Response;\n");
boolean isMutation = isMutationTool(baseName);
ToolDefinitionOptions opts = tool.definitionOptions() == null ? new ToolDefinitionOptions(null, null, null, null, null, null, null, null) : tool.definitionOptions();
methods.append(" @McpTool(name = \"").append(toToolName(moduleName, tool.group(), baseName))
methods.append(" @McpTool(name = \"").append(toToolName(moduleName, tool.group(), toolBaseName))
.append("\", title = \"").append(javaText(option(tool.title(), baseName)))
.append("\", description = \"").append(javaText(option(tool.description(), ""))).append("\")\n")
.append(" @GrowToolHint(\n")
@@ -339,10 +362,11 @@ public class ToolScaffolder {
StringBuilder fields = new StringBuilder();
StringBuilder methods = new StringBuilder();
for (ToolMethodDefinition tool : tools) {
String baseName = toPascalCase(tool.baseName());
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String toolBaseName = toPascalCase(tool.baseName());
String baseName = mci ? abbreviatedMciSourceBaseName(toolBaseName) : toolBaseName;
String clientClassName;
String clientVariable;
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String ioPrefix = (tool.clientSystemCode() != null && !tool.clientSystemCode().isBlank()) ? tool.clientSystemCode().toUpperCase() : tool.interfaceId();
if (mci) {
clientClassName = mciClientClassName(tool.clientSystemCode());
@@ -351,12 +375,17 @@ public class ToolScaffolder {
clientClassName = baseName + "Client";
clientVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Client";
}
String targetPkg = mci ? mciTargetSystemPackage(tool.clientSystemCode()) : null;
String converterPkg = bizPackage + ".converter" + (targetPkg != null ? "." + targetPkg : "");
String converterName = (mci && tool.clientSystemCode() != null && !tool.clientSystemCode().isBlank())
? tool.clientSystemCode().toUpperCase(Locale.ROOT) + "Converter"
: baseName + "Converter";
String converterVariable = "converter";
String integrationPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." + formatClientSystemCode(tool.clientSystemCode(), ".")
: ".infra.itrf.http." + toPackageSegment(tool.httpApiName()));
imports.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Request;\n")
.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Response;\n")
.append("import ").append(bizPackage).append(".converter.").append(baseName).append("Converter;\n")
.append("import ").append(converterPkg).append(".").append(converterName).append(";\n")
.append("import ").append(integrationPackage).append(".").append(clientClassName).append(";\n")
.append("import ").append(integrationPackage).append(".io.").append(mci ? ioPrefix + "_I;\n" : baseName + "HttpRequest;\n")
.append("import ").append(integrationPackage).append(".io.").append(mci ? ioPrefix + "_O;\n" : baseName + "HttpResponse;\n");
@@ -368,7 +397,7 @@ public class ToolScaffolder {
fields.append(" private final ").append(clientClassName).append(" ").append(clientVariable).append(";\n");
}
if (!fields.toString().contains(" " + converterVariable + ";")) {
fields.append(" private final ").append(baseName).append("Converter ").append(converterVariable).append(";\n");
fields.append(" private final ").append(converterName).append(" ").append(converterVariable).append(";\n");
}
methods.append(groupedToolMethodContent(tool, baseName, converterVariable, clientVariable, ioPrefix, mci));
}
@@ -444,15 +473,15 @@ public class ToolScaffolder {
+ "public interface " + useCaseBaseName + "Converter {\n}\n";
}
private static String groupedMciConverterContent(String bizPackage, String baseName, String ioPackage, String interfaceId) {
return "package " + bizPackage + ".converter;\n\n"
private static String groupedMciConverterContent(String converterPackage, String bizPackage, String baseName, String ioPackage, String interfaceId, String converterName) {
return "package " + converterPackage + ";\n\n"
+ "import " + bizPackage + ".dto." + baseName + "Request;\n"
+ "import " + bizPackage + ".dto." + baseName + "Response;\n"
+ "import " + ioPackage + ".io." + interfaceId + "_I;\n"
+ "import " + ioPackage + ".io." + interfaceId + "_O;\n"
+ "import org.mapstruct.Mapper;\nimport org.mapstruct.ReportingPolicy;\n\n"
+ "@Mapper(componentModel = \"spring\", unmappedTargetPolicy = ReportingPolicy.IGNORE)\n"
+ "public interface " + baseName + "Converter {\n"
+ "public interface " + converterName + " {\n"
+ " " + interfaceId + "_I toRequest(" + baseName + "Request request);\n"
+ " " + baseName + "Response toResponse(" + interfaceId + "_O response);\n}\n";
}
@@ -479,14 +508,15 @@ public class ToolScaffolder {
String useCase = Files.readString(useCaseFile, StandardCharsets.UTF_8);
String implementation = Files.readString(useCaseImplFile, StandardCharsets.UTF_8);
for (ToolMethodDefinition tool : tools) {
String baseName = toPascalCase(tool.baseName());
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String toolBaseName = toPascalCase(tool.baseName());
String baseName = mci ? abbreviatedMciSourceBaseName(toolBaseName) : toolBaseName;
String methodName = tool.methodName();
String toolName = toToolName(moduleName, tool.group(), baseName);
String toolName = toToolName(moduleName, tool.group(), toolBaseName);
if (useCase.matches("(?s).*\\b" + java.util.regex.Pattern.quote(methodName) + "\\s*\\(.*")
|| useCase.contains("name = \"" + toolName + "\"")) {
throw new IllegalArgumentException("Tool method or MCP Tool name already exists: " + methodName);
}
boolean mci = "MCI".equalsIgnoreCase(tool.routingType());
String integrationPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." + formatClientSystemCode(tool.clientSystemCode(), ".")
: ".infra.itrf.http." + toPackageSegment(tool.httpApiName()));
String requestType = baseName + "Request";
@@ -551,11 +581,16 @@ public class ToolScaffolder {
clientClassName = baseName + "Client";
clientVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Client";
}
String targetPkg = mci ? mciTargetSystemPackage(tool.clientSystemCode()) : null;
String converterPkg = bizPackage + ".converter" + (targetPkg != null ? "." + targetPkg : "");
String converterName = (mci && tool.clientSystemCode() != null && !tool.clientSystemCode().isBlank())
? tool.clientSystemCode().toUpperCase(Locale.ROOT) + "Converter"
: baseName + "Converter";
String converterVariable = "converter";
implementation = addImport(implementation, "import " + bizPackage + ".dto." + requestType + ";");
implementation = addImport(implementation, "import " + bizPackage + ".dto." + responseType + ";");
implementation = addImport(implementation, "import " + bizPackage + ".converter." + baseName + "Converter;");
implementation = addImport(implementation, "import " + converterPkg + "." + converterName + ";");
implementation = addImport(implementation, "import " + integrationPackage + "." + clientClassName + ";");
implementation = addImport(implementation, "import " + integrationPackage + ".io." + requestIo + ";");
implementation = addImport(implementation, "import " + integrationPackage + ".io." + responseIo + ";");
@@ -574,14 +609,15 @@ public class ToolScaffolder {
implementation = insertConstructorField(implementation, " private final " + clientClassName + " " + clientVariable + ";");
}
if (!implementation.contains(" " + converterVariable + ";")) {
implementation = insertConstructorField(implementation, " private final " + baseName + "Converter " + converterVariable + ";");
implementation = insertConstructorField(implementation, " private final " + converterName + " " + converterVariable + ";");
}
String method = groupedToolMethodContent(tool, baseName, converterVariable, clientVariable, ioPrefix, mci);
implementation = insertBeforeLastBrace(implementation, method);
}
writeUtf8(useCaseFile, useCase);
writeUtf8(useCaseImplFile, implementation);
if (!Files.exists(converterFile)) {
boolean hasMci = tools.stream().anyMatch(t -> "MCI".equalsIgnoreCase(t.routingType()));
if (!hasMci && !Files.exists(converterFile)) {
writeUtf8(converterFile, groupedConverterContent(bizPackage, useCaseBaseName, tools));
}
}
@@ -805,6 +841,9 @@ public class ToolScaffolder {
if (targetSystemPackage != null) {
converterPackage += "." + targetSystemPackage;
}
String converterClassName = (isMci && clientSystemCode != null && !clientSystemCode.isBlank())
? clientSystemCode.toUpperCase(Locale.ROOT) + "Converter"
: baseName + "Converter";
Path converterDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "converter"));
if (targetSystemPackage != null) {
for (String segment : targetSystemPackage.split("\\.")) {
@@ -1016,7 +1055,7 @@ public class ToolScaffolder {
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import %s.%sConverter;
import %s.%s;
import %s.%s.io.%s_I;
import %s.%s.io.%s_O;
%s
@@ -1041,7 +1080,7 @@ public class ToolScaffolder {
public class %sUseCaseImpl implements %sUseCase {
%s
private final %sConverter converter;
private final %s converter;
@Override
public %sResponse execute(%sRequest req) {
@@ -1090,7 +1129,7 @@ public class ToolScaffolder {
bizPackage, baseName,
bizPackage, baseName,
bizPackage, baseName,
converterPackage, baseName,
converterPackage, converterClassName,
BASE_PACKAGE, mciGroupPath.replace("/", "."), ioPrefix,
BASE_PACKAGE, mciGroupPath.replace("/", "."), ioPrefix,
(clientPrefixCap.isEmpty() ? "" : "import " + BASE_PACKAGE + "." + mciGroupPath.replace("/", ".") + ".Mci" + clientPrefixCap + "Client;\n"),
@@ -1102,7 +1141,7 @@ public class ToolScaffolder {
baseName,
baseName,
(clientPrefixCap.isEmpty() ? "private final AxhubMciComponent mci;" : "private final Mci" + clientPrefixCap + "Client mci;"),
baseName,
converterClassName,
baseName,
baseName,
toolName,
@@ -1307,8 +1346,8 @@ public class ToolScaffolder {
baseName, ioPrefix,
baseName, ioPrefix
);
converterContent = mciConverterContent(converterPackage, bizPackage, baseName, mciGroupPath.replace("/", "."), ioPrefix);
writeUtf8(converterDir.resolve(baseName + "Converter.java"), converterContent);
converterContent = mciConverterContent(converterPackage, bizPackage, baseName, mciGroupPath.replace("/", "."), ioPrefix, converterClassName);
writeUtf8(converterDir.resolve(converterClassName + ".java"), converterContent);
log.append("\n=========================================\n");
log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n");
@@ -1319,7 +1358,7 @@ public class ToolScaffolder {
log.append("[Response DTO] ").append(dtoDir.resolve(baseName + "Response.java")).append("\n");
log.append("[MCI Request IO] ").append(mciIoDir.resolve(interfaceId + "_I.java")).append("\n");
log.append("[MCI Response IO] ").append(mciIoDir.resolve(interfaceId + "_O.java")).append("\n");
log.append("[MCI Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n");
log.append("[MCI Converter] ").append(converterDir.resolve(converterClassName + ".java")).append("\n");
if (!clientPrefixCap.isEmpty()) {
String receiveServiceId = (clientSystemCode != null && !clientSystemCode.isBlank()) ? clientSystemCode.toUpperCase() : "";
@@ -2169,7 +2208,7 @@ public class ToolScaffolder {
return normalized.isBlank() ? "http_api" : normalized;
}
private static String mciConverterContent(String converterPackage, String bizPackage, String baseName,
String mciPackage, String interfaceId) {
String mciPackage, String interfaceId, String converterClassName) {
return """
package %s;
@@ -2182,7 +2221,7 @@ public class ToolScaffolder {
import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface %sConverter {
public interface %s {
// Field names differ? Add mappings like this before the method.
// @Mapping(source = "sourceField", target = "targetField")
%s_I toLegacyRequest(%sRequest request);
@@ -2191,7 +2230,7 @@ public class ToolScaffolder {
}
""".formatted(converterPackage, bizPackage, baseName, bizPackage, baseName,
BASE_PACKAGE, mciPackage, interfaceId, BASE_PACKAGE, mciPackage, interfaceId,
baseName, interfaceId, baseName, baseName, interfaceId, baseName, interfaceId);
converterClassName, interfaceId, baseName, baseName, interfaceId, baseName, interfaceId);
}
private static String mciTargetSystemPackage(String clientSystemCode) {

View File

@@ -550,8 +550,10 @@ class ToolScaffolderTest {
"ONBTA2380", null, null, List.of(), List.of(), null, null);
Path sourceRoot = root.resolve("dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro");
Path converter = sourceRoot.resolve("converter/nbt/a/IndiCustomConverter.java");
Path converter = sourceRoot.resolve("converter/nbt/a/ONBTA2380Converter.java");
assertTrue(Files.exists(converter));
assertFalse(Files.exists(sourceRoot.resolve("converter/IndiCustomConverter.java")));
assertFalse(Files.exists(sourceRoot.resolve("converter/nbt/a/IndiCustomConverter.java")));
assertTrue(Files.exists(sourceRoot.resolve("dto/IndiCustomRequest.java")));
assertTrue(Files.exists(sourceRoot.resolve("dto/IndiCustomResponse.java")));
assertTrue(Files.exists(sourceRoot.resolve("usecase/IndiCustomUseCase.java")));
@@ -561,7 +563,37 @@ class ToolScaffolderTest {
String useCaseSource = Files.readString(sourceRoot.resolve("usecase/IndiCustomUseCase.java"));
String implementationSource = Files.readString(sourceRoot.resolve("usecase/impl/IndiCustomUseCaseImpl.java"));
assertTrue(converterSource.contains("package io.shinhanlife.dat.mcc.biz.pro.converter.nbt.a;"), converterSource);
assertTrue(implementationSource.contains("import io.shinhanlife.dat.mcc.biz.pro.converter.nbt.a.IndiCustomConverter;"), implementationSource);
assertTrue(converterSource.contains("public interface ONBTA2380Converter {"), converterSource);
assertTrue(implementationSource.contains("import io.shinhanlife.dat.mcc.biz.pro.converter.nbt.a.ONBTA2380Converter;"), implementationSource);
assertTrue(implementationSource.contains("private final ONBTA2380Converter converter;"), implementationSource);
assertTrue(useCaseSource.contains("name = \"pro_individual_inquiry\""), useCaseSource);
}
@Test
void generatesAbbreviatedMciSourcesAndTargetSystemConverterInGroupMode() throws Exception {
String moduleName = root.resolve("dat-was-pro").toString();
ToolScaffolder.scaffoldUseCase("IndividualCustomerDetailInquiry", moduleName, "tester", "2026.09.10", List.of(
new ToolScaffolder.ToolMethodDefinition("IndividualCustomerDetailInquiry", "inquiry", "LCHITP00001",
"개인 고객 상세 조회", "개인 고객 상세 정보를 조회합니다.", "pro", "MCI", false,
"ONBTA2380", null, List.of(), List.of(), null)));
Path sourceRoot = root.resolve("dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro");
Path converter = sourceRoot.resolve("converter/nbt/a/ONBTA2380Converter.java");
assertTrue(Files.exists(converter));
assertFalse(Files.exists(sourceRoot.resolve("converter/IndiCustomConverter.java")));
assertTrue(Files.exists(sourceRoot.resolve("dto/IndiCustomRequest.java")));
assertTrue(Files.exists(sourceRoot.resolve("dto/IndiCustomResponse.java")));
assertTrue(Files.exists(sourceRoot.resolve("usecase/IndiCustomUseCase.java")));
assertTrue(Files.exists(sourceRoot.resolve("usecase/impl/IndiCustomUseCaseImpl.java")));
String converterSource = Files.readString(converter);
String useCaseSource = Files.readString(sourceRoot.resolve("usecase/IndiCustomUseCase.java"));
String implementationSource = Files.readString(sourceRoot.resolve("usecase/impl/IndiCustomUseCaseImpl.java"));
assertTrue(converterSource.contains("package io.shinhanlife.dat.mcc.biz.pro.converter.nbt.a;"), converterSource);
assertTrue(converterSource.contains("public interface ONBTA2380Converter {"), converterSource);
assertTrue(implementationSource.contains("import io.shinhanlife.dat.mcc.biz.pro.converter.nbt.a.ONBTA2380Converter;"), implementationSource);
assertTrue(implementationSource.contains("private final ONBTA2380Converter converter;"), implementationSource);
assertTrue(useCaseSource.contains("name = \"pro_individual_inquiry\""), useCaseSource);
}
}