Browse Source

Merge pull request #15372 from dashevchenko/openApiSpecFix

Updated swagger configuration to fix AiChatModelConfig children schemas
pull/15375/head
Viacheslav Klimov 6 months ago
committed by GitHub
parent
commit
75b060aa1d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 41
      application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

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

@ -62,6 +62,7 @@ import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.ai.model.chat.AiChatModelConfig;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse;
@ -77,6 +78,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@ -361,7 +363,8 @@ public class SwaggerConfiguration {
// (e.g. CalculatedField via EntityExportData.calculatedFields) to prevent
// field-level ignore lists from polluting the global schema when resolution
// order varies.
.addSchemas("CalculatedField", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(CalculatedField.class)).schema);
.addSchemas("CalculatedField", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(CalculatedField.class)).schema)
.addSchemas("AiChatModelConfig", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(AiChatModelConfig.class)).schema);
}
private OperationCustomizer operationCustomizer() {
@ -432,20 +435,40 @@ public class SwaggerConfiguration {
log.debug("Removed duplicate schema '{}' (base '{}' exists)", name, baseName);
}
// Remove duplicate inline entries in allOf (springdoc can generate identical
// property blocks when resolving a type through multiple parent paths).
// Remove duplicate or redundant inline entries in allOf. Springdoc can
// generate multiple inline property blocks when resolving a type through
// multiple parent paths (e.g. record + sealed interface). One block may be
// a strict subset of another (same properties, but the superset has extras
// like "modelType"). Keep only the superset in that case.
schemas.values().forEach(schema -> {
if (schema.getAllOf() != null && schema.getAllOf().size() > 1) {
List<Schema> allOf = schema.getAllOf();
List<Schema> deduplicated = new ArrayList<>();
for (Schema entry : allOf) {
if (deduplicated.stream().noneMatch(entry::equals)) {
deduplicated.add(entry);
Set<Integer> redundant = new HashSet<>();
for (int i = 0; i < allOf.size(); i++) {
if (redundant.contains(i)) continue;
Schema a = allOf.get(i);
if (a.get$ref() != null || a.getProperties() == null) continue;
for (int j = i + 1; j < allOf.size(); j++) {
if (redundant.contains(j)) continue;
Schema b = allOf.get(j);
if (b.get$ref() != null || b.getProperties() == null) continue;
if (a.getProperties().entrySet().containsAll(b.getProperties().entrySet())) {
redundant.add(j); // b is a subset of a
} else if (b.getProperties().entrySet().containsAll(a.getProperties().entrySet())) {
redundant.add(i); // a is a subset of b
break;
}
}
}
if (deduplicated.size() < allOf.size()) {
if (!redundant.isEmpty()) {
List<Schema> filtered = new ArrayList<>();
for (int i = 0; i < allOf.size(); i++) {
if (!redundant.contains(i)) {
filtered.add(allOf.get(i));
}
}
allOf.clear();
allOf.addAll(deduplicated);
allOf.addAll(filtered);
}
}
});

Loading…
Cancel
Save