feat: scaffold tools from declared fields
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 1m39s
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 1m39s
This commit is contained in:
@@ -7,6 +7,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Scanner;
|
||||
|
||||
@@ -41,6 +42,9 @@ public class ToolScaffolder {
|
||||
private static final String BASE_PACKAGE = "io.shinhanlife.dap.mcc";
|
||||
private static final String BASE_PACKAGE_PATH = "src/main/java/io/shinhanlife/dap/mcc";
|
||||
|
||||
public record FieldDefinition(String name, String type, String description, String example, boolean required) {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
|
||||
@@ -94,6 +98,12 @@ public class ToolScaffolder {
|
||||
}
|
||||
|
||||
public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource) throws IOException {
|
||||
return scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate,
|
||||
register, clientSystemCode, inputSchemaResource, outputSchemaResource,
|
||||
List.of(new FieldDefinition("query", "String", "Search query", "example", false)), List.of());
|
||||
}
|
||||
|
||||
public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List<FieldDefinition> inputFields, List<FieldDefinition> outputFields) throws IOException {
|
||||
baseName = toPascalCase(baseName);
|
||||
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
|
||||
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
|
||||
@@ -185,6 +195,7 @@ public class ToolScaffolder {
|
||||
.replaceAll("(?m)^\\s*-\\(\\?:[^\\r\\n]*\\R", "")
|
||||
.replace("private String phoneNumber;", "@Schema(example = \"01012345678\")\n private String phoneNumber;")
|
||||
.replace("private String message;", "@Schema(example = \"테스트 메시지입니다.\")\n private String message;");
|
||||
reqContent = dtoContent(bizPackage, baseName + "Request", inputFields, author, createDate, true);
|
||||
Files.writeString(dtoDir.resolve(baseName + "Request.java"), reqContent);
|
||||
|
||||
// Generate Response DTO
|
||||
@@ -218,6 +229,7 @@ public class ToolScaffolder {
|
||||
// TODO: Add response fields here. Do not include PII in the Tool response.
|
||||
}
|
||||
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
|
||||
resContent = dtoContent(bizPackage, baseName + "Response", outputFields, author, createDate, false);
|
||||
Files.writeString(dtoDir.resolve(baseName + "Response.java"), resContent);
|
||||
|
||||
String toolName = toToolName(moduleName, group, baseName);
|
||||
@@ -368,6 +380,14 @@ public class ToolScaffolder {
|
||||
baseName,
|
||||
baseName
|
||||
);
|
||||
String mciIoPackage = BASE_PACKAGE + "." + mciGroupPath.replace("/", ".");
|
||||
serviceImplContent = serviceImplContent
|
||||
.replace("import " + mciIoPackage + "." + interfaceId + "_I;",
|
||||
"import " + mciIoPackage + "." + interfaceId + "_I;\nimport " + mciIoPackage + "." + interfaceId + "_O;")
|
||||
.replace("Transfer<Object> resTransfer", "Transfer<" + interfaceId + "_O> resTransfer")
|
||||
.replace("Object.class", interfaceId + "_O.class")
|
||||
.replace("response.setResultCode(\"SUCCESS\");",
|
||||
"if (resTransfer.getBody() != null) {\n response = converter.toResponse(resTransfer.getBody());\n }\n response.setResultCode(\"SUCCESS\");");
|
||||
} else {
|
||||
serviceImplContent = """
|
||||
package %s.usecase.impl;
|
||||
@@ -461,6 +481,7 @@ public class ToolScaffolder {
|
||||
private String content;
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId);
|
||||
mciReqContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_I", inputFields, author, createDate);
|
||||
Files.writeString(mciIoDir.resolve(interfaceId + "_I.java"), mciReqContent);
|
||||
|
||||
String mciResContent = """
|
||||
@@ -487,6 +508,7 @@ public class ToolScaffolder {
|
||||
// TODO: Add response fields here
|
||||
}
|
||||
""".formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId);
|
||||
mciResContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_O", outputFields, author, createDate);
|
||||
Files.writeString(mciIoDir.resolve(interfaceId + "_O.java"), mciResContent);
|
||||
|
||||
String converterContent = """
|
||||
@@ -538,6 +560,7 @@ public class ToolScaffolder {
|
||||
baseName, interfaceId,
|
||||
baseName, interfaceId
|
||||
);
|
||||
converterContent = mciConverterContent(bizPackage, baseName, mciGroupPath.replace("/", "."), interfaceId);
|
||||
Files.writeString(converterDir.resolve(baseName + "Converter.java"), converterContent);
|
||||
|
||||
log.append("\n=========================================\n");
|
||||
@@ -579,7 +602,7 @@ public class ToolScaffolder {
|
||||
public class Mci%sClient {
|
||||
private final AxhubMciComponent mci;
|
||||
|
||||
public Transfer<Object> callTo(String interfaceId, String dummy, Object mciReq, Class<Object> resType) throws Exception {
|
||||
public <O> Transfer<O> callTo(String interfaceId, String dummy, Object mciReq, Class<O> resType) throws Exception {
|
||||
return mci.callTo(interfaceId, dummy, mciReq, resType);
|
||||
}
|
||||
}
|
||||
@@ -764,6 +787,84 @@ public class ToolScaffolder {
|
||||
.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static String dtoContent(String packageName, String className, List<FieldDefinition> fields,
|
||||
String author, String createDate, boolean request) {
|
||||
String body = fieldLines(fields);
|
||||
if (!request) {
|
||||
body = " private String resultCode;\n\n private String resultMessage;\n" + body;
|
||||
}
|
||||
return """
|
||||
package %s;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class %s {
|
||||
%s}
|
||||
""".formatted(packageName, className, body);
|
||||
}
|
||||
|
||||
private static String mciIoContent(String packageSuffix, String className, List<FieldDefinition> fields,
|
||||
String author, String createDate) {
|
||||
return """
|
||||
package %s.%s;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class %s {
|
||||
%s}
|
||||
""".formatted(BASE_PACKAGE, packageSuffix, className, fieldLines(fields));
|
||||
}
|
||||
|
||||
private static String mciConverterContent(String bizPackage, String baseName, String mciPackage, String interfaceId) {
|
||||
return """
|
||||
package %s.converter;
|
||||
|
||||
import %s.dto.%sRequest;
|
||||
import %s.dto.%sResponse;
|
||||
import %s.%s.io.%s_I;
|
||||
import %s.%s.io.%s_O;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface %sConverter {
|
||||
%s_I toLegacyRequest(%sRequest request);
|
||||
%sRequest toRequest(%s_I mciRequest);
|
||||
%sResponse toResponse(%s_O mciRes);
|
||||
}
|
||||
""".formatted(bizPackage, bizPackage, baseName, bizPackage, baseName,
|
||||
BASE_PACKAGE, mciPackage, interfaceId, BASE_PACKAGE, mciPackage, interfaceId,
|
||||
baseName, interfaceId, baseName, baseName, interfaceId, baseName, interfaceId);
|
||||
}
|
||||
|
||||
private static String fieldLines(List<FieldDefinition> fields) {
|
||||
StringBuilder source = new StringBuilder();
|
||||
for (FieldDefinition field : fields == null ? List.<FieldDefinition>of() : fields) {
|
||||
String type = supportedType(field.type());
|
||||
String description = field.description() == null ? "" : field.description().replace("\"", "\\\"");
|
||||
String example = field.example() == null ? "" : field.example().replace("\"", "\\\"");
|
||||
source.append(" @Schema(description = \"").append(description).append("\", example = \"")
|
||||
.append(example).append("\"");
|
||||
if (field.required()) {
|
||||
source.append(", requiredMode = Schema.RequiredMode.REQUIRED");
|
||||
}
|
||||
source.append(")\n private ").append(type).append(' ').append(field.name()).append(";\n\n");
|
||||
}
|
||||
return source.toString();
|
||||
}
|
||||
|
||||
private static String supportedType(String type) {
|
||||
return switch (type == null ? "String" : type) {
|
||||
case "String", "Integer", "Long", "Double", "Boolean", "BigDecimal" -> type;
|
||||
default -> throw new IllegalArgumentException("Unsupported field type: " + type);
|
||||
};
|
||||
}
|
||||
|
||||
private static String toToolName(String moduleName, String group, String baseName) {
|
||||
String moduleDirectory = Path.of(moduleName).getFileName().toString();
|
||||
String pod = moduleDirectory.startsWith("dap-was-")
|
||||
|
||||
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
@@ -78,4 +79,32 @@ class ToolScaffolderTest {
|
||||
assertTrue(implementation.contains("public SearchHrResponse execute(SearchHrRequest req)"));
|
||||
assertTrue(implementation.contains("response.setResultCode(\"SUCCESS\")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesMciToolFromDeclaredInputAndOutputFields() throws Exception {
|
||||
String moduleName = root.resolve("dap-was-pay").toString();
|
||||
List<ToolScaffolder.FieldDefinition> inputFields = List.of(
|
||||
new ToolScaffolder.FieldDefinition("employeeId", "String", "Employee identifier", "EMP10001", true),
|
||||
new ToolScaffolder.FieldDefinition("page", "Integer", "Page number", "1", false));
|
||||
List<ToolScaffolder.FieldDefinition> outputFields = List.of(
|
||||
new ToolScaffolder.FieldDefinition("employeeName", "String", "Employee name", "Hong Gildong", true));
|
||||
|
||||
ToolScaffolder.scaffold("search hr", "SHEARCH_01", "HR search", "pay", "MCI", moduleName,
|
||||
"tester", "2026.08.09", true, "DFAG", null, null, inputFields, outputFields);
|
||||
|
||||
Path sourceRoot = root.resolve("dap-was-pay/src/main/java/io/shinhanlife/dap/mcc");
|
||||
String request = Files.readString(sourceRoot.resolve("biz/pay/dto/SearchHrRequest.java"));
|
||||
String response = Files.readString(sourceRoot.resolve("biz/pay/dto/SearchHrResponse.java"));
|
||||
String mciRequest = Files.readString(sourceRoot.resolve("infra/itrf/mci/dfa/g/io/SHEARCH_01_I.java"));
|
||||
String mciResponse = Files.readString(sourceRoot.resolve("infra/itrf/mci/dfa/g/io/SHEARCH_01_O.java"));
|
||||
String converter = Files.readString(sourceRoot.resolve("biz/pay/converter/SearchHrConverter.java"));
|
||||
|
||||
assertTrue(request.contains("private String employeeId;"));
|
||||
assertTrue(request.contains("private Integer page;"));
|
||||
assertFalse(request.contains("phoneNumber"));
|
||||
assertTrue(response.contains("private String employeeName;"));
|
||||
assertTrue(mciRequest.contains("private String employeeId;"));
|
||||
assertTrue(mciResponse.contains("private String employeeName;"));
|
||||
assertTrue(converter.contains("SearchHrResponse toResponse(SHEARCH_01_O mciRes);"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user