From 3ad2f762e0f057f5adc8e07361db382af0e0d269 Mon Sep 17 00:00:00 2001 From: "kirill.labutin" Date: Mon, 12 Jan 2026 18:49:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=81=D1=82=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=20JSON-R?= =?UTF-8?q?PC=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B0=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20JS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-generator/pom.xml | 22 ++ .../JavascriptClientGenerator.java | 198 ++++++++++++++++++ pom.xml | 1 + web-ui/pom.xml | 20 ++ 4 files changed, 241 insertions(+) create mode 100644 api-generator/src/main/java/ru/kirillius/XCP/ApiGenerator/JavascriptClientGenerator.java create mode 100644 web-ui/pom.xml diff --git a/api-generator/pom.xml b/api-generator/pom.xml index 948579c..88a8f65 100644 --- a/api-generator/pom.xml +++ b/api-generator/pom.xml @@ -18,4 +18,26 @@ 1.0.0.0 + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + generate-js-client + generate-sources + + java + + + ru.kirillius.XCP.ApiGenerator.JavascriptClientGenerator + + + + + + \ No newline at end of file diff --git a/api-generator/src/main/java/ru/kirillius/XCP/ApiGenerator/JavascriptClientGenerator.java b/api-generator/src/main/java/ru/kirillius/XCP/ApiGenerator/JavascriptClientGenerator.java new file mode 100644 index 0000000..f06292a --- /dev/null +++ b/api-generator/src/main/java/ru/kirillius/XCP/ApiGenerator/JavascriptClientGenerator.java @@ -0,0 +1,198 @@ +package ru.kirillius.XCP.ApiGenerator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class JavascriptClientGenerator { + + private static final String SERVICES_PACKAGE = "ru.kirillius.XCP.RPC.Services"; + private static final String OUTPUT_DIR = "target/generated-sources/api.js"; + + public static void main(String[] args) { + try { + generateJavascriptClient(); + System.out.println("JavaScript API client generated successfully at: " + OUTPUT_DIR); + } catch (Exception e) { + System.err.println("Failed to generate JavaScript client: " + e.getMessage()); + e.printStackTrace(); + } + } + + public static void generateJavascriptClient() throws IOException, ClassNotFoundException { + StringBuilder jsBuilder = new StringBuilder(); + jsBuilder.append("API = {\n"); + + List> serviceClasses = findServiceClasses(); + boolean first = true; + + for (Class serviceClass : serviceClasses) { + if (!first) { + jsBuilder.append(",\n"); + } + first = false; + + String className = serviceClass.getSimpleName(); + jsBuilder.append(" ").append(className).append(" : {\n"); + + Method[] methods = serviceClass.getDeclaredMethods(); + List rpcMethods = new ArrayList<>(); + + for (Method method : methods) { + Annotation[] annotations = method.getAnnotations(); + for (Annotation annotation : annotations) { + if (annotation.annotationType().getSimpleName().equals("JsonRpcMethod")) { + rpcMethods.add(method); + break; + } + } + } + + for (int i = 0; i < rpcMethods.size(); i++) { + Method method = rpcMethods.get(i); + Annotation jsonRpcMethod = null; + + for (Annotation annotation : method.getAnnotations()) { + if (annotation.annotationType().getSimpleName().equals("JsonRpcMethod")) { + jsonRpcMethod = annotation; + break; + } + } + + generateMethod(jsBuilder, method, jsonRpcMethod, className); + + if (i < rpcMethods.size() - 1) { + jsBuilder.append(",\n"); + } else { + jsBuilder.append("\n"); + } + } + + jsBuilder.append(" }"); + } + + jsBuilder.append("\n}\n"); + + File outputFile = new File(OUTPUT_DIR); + outputFile.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(outputFile)) { + writer.write(jsBuilder.toString()); + } + } + + private static List> findServiceClasses() throws ClassNotFoundException { + List> serviceClasses = new ArrayList<>(); + + serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".Auth")); + serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".Profile")); + serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".UserManagement")); + + return serviceClasses; + } + + private static void generateMethod(StringBuilder builder, Method method, Annotation jsonRpcMethod, String className) { + String methodName = method.getName(); + + builder.append(" /**\n"); + + try { + Object description = jsonRpcMethod.annotationType().getMethod("description").invoke(jsonRpcMethod); + builder.append(" * ").append(description).append("\n"); + + Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod); + if (parameters instanceof Object[]) { + Object[] params = (Object[]) parameters; + for (Object param : params) { + Class paramClass = param.getClass(); + Object name = paramClass.getMethod("name").invoke(param); + Object optional = paramClass.getMethod("optional").invoke(param); + + builder.append(" * @param ").append(name); + if (!(Boolean) optional) { + builder.append(" (required)"); + } else { + builder.append(" (optional)"); + } + builder.append("\n"); + } + } + + Object returnType = jsonRpcMethod.annotationType().getMethod("returnType").invoke(jsonRpcMethod); + String returnTypeName = getSimpleTypeName((Class) returnType); + builder.append(" * @returns ").append(returnTypeName).append("\n"); + } catch (Exception e) { + builder.append(" * Method: ").append(methodName).append("\n"); + builder.append(" * @returns unknown\n"); + } + + builder.append(" */\n"); + + builder.append(" ").append(methodName).append(" : async function("); + + List paramNames = new ArrayList<>(); + try { + Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod); + if (parameters instanceof Object[]) { + Object[] params = (Object[]) parameters; + for (Object param : params) { + Object name = param.getClass().getMethod("name").invoke(param); + Object optional = param.getClass().getMethod("optional").invoke(param); + if (!(Boolean) optional) { + paramNames.add(name.toString()); + } + } + } + } catch (Exception e) { + // No parameters available + } + + builder.append(String.join(", ", paramNames)); + builder.append(") {\n"); + builder.append(" return await JSONRPC.invoke(\"").append(className).append(".").append(methodName).append("\", {"); + + List paramPairs = new ArrayList<>(); + try { + Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod); + if (parameters instanceof Object[]) { + Object[] params = (Object[]) parameters; + for (Object param : params) { + Object name = param.getClass().getMethod("name").invoke(param); + Object optional = param.getClass().getMethod("optional").invoke(param); + if (!(Boolean) optional) { + paramPairs.add(name.toString() + ":" + name.toString()); + } + } + } + } catch (Exception e) { + // No parameters available + } + + builder.append(String.join(", ", paramPairs)); + builder.append("});\n"); + builder.append(" }"); + } + + private static String getSimpleTypeName(Class type) { + if (type == null) return "unknown"; + if (type == boolean.class) return "boolean"; + if (type == int.class || type == long.class) return "number"; + if (type == String.class) return "string"; + if (type == void.class || type == Void.class) return "void"; + + String typeName = type.getSimpleName(); + if (type.isArray()) { + return "Array"; + } + + if (typeName.contains("Node") || typeName.contains("Object")) { + return "Object"; + } + + return typeName; + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index ac1266d..08da4ad 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ web-server app logging + web-ui diff --git a/web-ui/pom.xml b/web-ui/pom.xml new file mode 100644 index 0000000..c28bccc --- /dev/null +++ b/web-ui/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + ru.kirillius + XCP + 1.0.0.0 + + + web-ui + + + 21 + 21 + UTF-8 + + + \ No newline at end of file