Browse Source

openapi schema fields order fix

pull/15166/head
dashevchenko 7 months ago
parent
commit
a1758b5ddb
  1. 47
      application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

47
application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

@ -61,15 +61,18 @@ import org.springframework.http.HttpStatus;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse; import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse;
import org.thingsboard.server.exception.ThingsboardErrorResponse; import org.thingsboard.server.exception.ThingsboardErrorResponse;
import org.thingsboard.server.service.security.auth.rest.LoginRequest; import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import org.thingsboard.server.service.security.auth.rest.LoginResponse; import org.thingsboard.server.service.security.auth.rest.LoginResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -303,15 +306,14 @@ public class SwaggerConfiguration {
} }
} else if (schema != null && schema.getProperties() != null && !schema.getProperties().isEmpty()) { } else if (schema != null && schema.getProperties() != null && !schema.getProperties().isEmpty()) {
try { try {
var beanDesc = Json.mapper().getSerializationConfig().introspect(javaType); var beanDesc = JacksonUtil.OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
var orderedNames = beanDesc.findProperties().stream() var orderedNames = resolvePropertyOrder(cls, beanDesc);
.map(p -> p.getName())
.toList();
if (!orderedNames.isEmpty()) { if (!orderedNames.isEmpty()) {
@SuppressWarnings("unchecked")
Map<String, Schema> current = schema.getProperties(); Map<String, Schema> current = schema.getProperties();
var reordered = new LinkedHashMap<String, Object>(); var reordered = new LinkedHashMap<String, Schema>();
for (String name : orderedNames) { for (String name : orderedNames) {
Object prop = current.get(name); Schema prop = current.get(name);
if (prop != null) reordered.put(name, prop); if (prop != null) reordered.put(name, prop);
} }
current.forEach((k, v) -> reordered.putIfAbsent(k, v)); current.forEach((k, v) -> reordered.putIfAbsent(k, v));
@ -674,6 +676,37 @@ public class SwaggerConfiguration {
return new ApiResponse().description(description).content(content); return new ApiResponse().description(description).content(content);
} }
private static List<String> resolvePropertyOrder(Class<?> cls, com.fasterxml.jackson.databind.BeanDescription beanDesc) {
// Map backing field names to their JSON property names (respects @JsonProperty)
Map<String, String> fieldToJsonName = new LinkedHashMap<>();
LinkedHashSet<String> getterOnlyNames = new LinkedHashSet<>();
for (var prop : beanDesc.findProperties()) {
if (prop.getField() != null) {
fieldToJsonName.put(prop.getField().getName(), prop.getName());
} else {
getterOnlyNames.add(prop.getName());
}
}
// Walk class hierarchy (superclass first) to get field declaration order
List<Class<?>> hierarchy = new ArrayList<>();
for (Class<?> c = cls; c != null && c != Object.class; c = c.getSuperclass()) {
hierarchy.add(0, c);
}
List<String> ordered = new ArrayList<>();
for (Class<?> c : hierarchy) {
for (Field f : c.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) continue;
String jsonName = fieldToJsonName.get(f.getName());
if (jsonName != null) ordered.add(jsonName);
}
}
// Append getter-only properties (no backing field) at the end
ordered.addAll(getterOnlyNames);
return ordered;
}
private static Example errorExample(String summary, ThingsboardErrorResponse example) { private static Example errorExample(String summary, ThingsboardErrorResponse example) {
var node = (ObjectNode) JacksonUtil.valueToTree(example); var node = (ObjectNode) JacksonUtil.valueToTree(example);
node.put("timestamp", 1609459200000L); node.put("timestamp", 1609459200000L);

Loading…
Cancel
Save