Удалил SerializationUtils, добавил генератор спецификации api

This commit is contained in:
kirillius 2026-01-14 18:05:40 +03:00
parent 3ad2f762e0
commit 381fda5252
7 changed files with 143 additions and 13 deletions

View File

@ -9,7 +9,7 @@
<version>1.0.0.0</version>
</parent>
<artifactId>api-generator</artifactId>
<artifactId>api-spec-generator</artifactId>
<dependencies>
<dependency>
@ -27,13 +27,18 @@
<version>3.1.0</version>
<executions>
<execution>
<id>generate-js-client</id>
<phase>generate-sources</phase>
<id>generate-spec</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>ru.kirillius.XCP.ApiGenerator.JavascriptClientGenerator</mainClass>
<mainClass>ru.kirillius.XCP.ApiGenerator.SpecGenerator</mainClass>
<arguments>
<argument>rpc/src/main/java</argument>
<argument>ru.kirillius.XCP.RPC.Services</argument>
<argument>target/generated-sources/api.spec.json</argument>
</arguments>
</configuration>
</execution>
</executions>

View File

@ -0,0 +1,125 @@
package ru.kirillius.XCP.ApiGenerator;
import lombok.SneakyThrows;
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcMethod;
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcService;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.io.*;
import java.util.Collection;
import java.util.Objects;
import java.util.regex.Pattern;
public class SpecGenerator {
/**
* Args:
* 1 - path to find classes
* 2 - search in package
* 3 - output spec file path
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
var generator = new SpecGenerator(
new File(args[0]),
args[1],
new File(args[2])
);
generator.generate();
generator.writeSpecs();
}
private final File scanDir;
private final String packageName;
private final File outputFile;
public SpecGenerator(File scanDir, String packageName, File outputFile) {
this.scanDir = scanDir;
this.packageName = packageName;
this.outputFile = outputFile;
}
private final ObjectNode specs = JsonNodeFactory.instance.objectNode();
@SneakyThrows
public void generate() {
specs.removeAll();
var scanPath = new File(scanDir, packageName.replaceAll(Pattern.quote("."), "/"));
for (var file : Objects.requireNonNull(scanPath.listFiles())) {
if (!file.getName().endsWith(".java") || file.getName().contains("$")) {
continue;
}
var className = packageName + '.' + file.getName();
className = className.substring(0, className.length() - ".java".length());
var cls = Class.forName(className);
if (!JsonRpcService.class.isAssignableFrom(cls)) {
continue;
}
var classSpecs = specs.putObject(cls.getSimpleName());
classSpecs.put("name", cls.getSimpleName());
var methods = classSpecs.putArray("methods");
for (var method : cls.getDeclaredMethods()) {
var descriptor = method.getAnnotation(JsonRpcMethod.class);
if (descriptor == null) {
continue;
}
var methodSpecs = methods.addObject();
methodSpecs.put("name", method.getName());
methodSpecs.put("description", descriptor.description());
methodSpecs.put("return", getTypeName(descriptor.returnType()));
methodSpecs.put("accessLevel", descriptor.accessLevel().name());
var params = methodSpecs.putArray("params");
for (var parameter : descriptor.parameters()) {
var paramSpecs = params.addObject();
paramSpecs.put("name", parameter.name());
paramSpecs.put("type", getTypeName(parameter.type()));
paramSpecs.put("description", parameter.description());
paramSpecs.put("optional", parameter.optional());
}
methodSpecs.put("return", getTypeName(descriptor.returnType()));
}
}
}
public void writeSpecs() throws IOException {
outputFile.getParentFile().mkdirs();
try (var stream = new FileOutputStream(outputFile)) {
var mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(stream, specs);
}
}
private static String getTypeName(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";
if (type.isArray() || Collection.class.isAssignableFrom(type) || type == ArrayNode.class) {
return "Array";
}
if (JsonNode.class.isAssignableFrom(type)) {
return "Object";
}
return type.getSimpleName();
}
}

View File

@ -8,7 +8,7 @@ import org.hibernate.annotations.UuidGenerator;
import ru.kirillius.XCP.Commons.StreamHandler;
import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.*;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.io.IOException;
@ -157,7 +157,7 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
@JsonProperty
@Getter
@Setter
private ObjectNode properties = SerializationUtils.EmptyObject();
private ObjectNode properties = JsonNodeFactory.instance.objectNode();
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER)

View File

@ -11,9 +11,9 @@ import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.Entities.Input;
import ru.kirillius.XCP.Persistence.*;
import ru.kirillius.XCP.Serialization.PollSettingsConverter;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.util.HashSet;
@ -110,7 +110,7 @@ public class InputRepositoryImpl extends AbstractNodeRepository<Input> implement
@JsonProperty
@Getter
@Setter
private ObjectNode properties = SerializationUtils.EmptyObject();
private ObjectNode properties = JsonNodeFactory.instance.objectNode();
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER)

View File

@ -9,8 +9,8 @@ import ru.kirillius.XCP.Data.ValueTransformationChain;
import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.Entities.Output;
import ru.kirillius.XCP.Persistence.*;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.util.HashSet;
@ -106,7 +106,7 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
@JsonProperty
@Getter
@Setter
private ObjectNode properties = SerializationUtils.EmptyObject();
private ObjectNode properties = JsonNodeFactory.instance.objectNode();
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER)

View File

@ -12,7 +12,7 @@ import ru.kirillius.XCP.Persistence.Entities.User;
import ru.kirillius.XCP.Persistence.EntityImplementation;
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
import ru.kirillius.XCP.Security.UserRole;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.io.IOException;
@ -80,7 +80,7 @@ public class UserRepositoryImpl extends AbstractRepository<User> implements User
@Getter
@Setter
@JsonProperty
private ObjectNode values = SerializationUtils.EmptyObject();
private ObjectNode values = JsonNodeFactory.instance.objectNode();
@Override
public void setPassword(String password) {

View File

@ -10,7 +10,7 @@
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>api-generator</module>
<module>api-spec-generator</module>
<module>database</module>
<module>core</module>
<module>rpc</module>