diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 8b3bb67eef..fadf26abea 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -76,6 +76,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; @@ -406,6 +407,10 @@ public class SwaggerConfiguration { } }); + // Deduplicate allOf child schemas: remove properties that are already defined + // in the referenced parent schema to avoid duplication (e.g. EntityId children) + schemas.values().forEach(schema -> deduplicateAllOfProperties(schema, schemas)); + // Fix polymorphic request/response bodies: replace inline oneOf with base type $ref paths.values().stream() .flatMap(pathItem -> pathItem.readOperationsMap().values().stream()) @@ -676,6 +681,55 @@ public class SwaggerConfiguration { return new ApiResponse().description(description).content(content); } + @SuppressWarnings("unchecked") + private void deduplicateAllOfProperties(Schema schema, Map allSchemas) { + if (schema.getAllOf() == null) { + return; + } + + // Collect properties defined in any $ref'd parent within the allOf + Set parentProperties = new LinkedHashSet<>(); + for (Schema allOfElement : schema.getAllOf()) { + String ref = allOfElement.get$ref(); + if (ref != null) { + String refName = ref.substring(ref.lastIndexOf('/') + 1); + Schema parent = allSchemas.get(refName); + if (parent != null && parent.getProperties() != null) { + parentProperties.addAll(parent.getProperties().keySet()); + } + } + } + + if (parentProperties.isEmpty()) { + return; + } + + // Strip those properties from inline (non-$ref) allOf elements + schema.getAllOf().removeIf(allOfElement -> { + if (allOfElement.get$ref() != null) { + return false; + } + if (allOfElement.getProperties() != null) { + parentProperties.forEach(prop -> allOfElement.getProperties().remove(prop)); + if (allOfElement.getProperties().isEmpty()) { + allOfElement.setProperties(null); + } + } + // Remove the inline element entirely if it has nothing left + return allOfElement.getProperties() == null + && allOfElement.getRequired() == null + && allOfElement.getType() == null; + }); + + // Remove required entries at the schema level that are already required by the parent + if (schema.getRequired() != null) { + schema.getRequired().removeAll(parentProperties); + if (schema.getRequired().isEmpty()) { + schema.setRequired(null); + } + } + } + private static List resolvePropertyOrder(Class cls, com.fasterxml.jackson.databind.BeanDescription beanDesc) { // Map backing field names to their JSON property names (respects @JsonProperty) Map fieldToJsonName = new LinkedHashMap<>(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java index 7bb3c367ec..ba4f8c9c56 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java @@ -27,8 +27,8 @@ import java.util.UUID; @Schema(description = "Default notification rule recipients configuration", allOf = NotificationRuleRecipientsConfig.class) @Data -@EqualsAndHashCode(callSuper = true) -public abstract class DefaultNotificationRuleRecipientsConfig extends NotificationRuleRecipientsConfig { +@EqualsAndHashCode +public abstract class DefaultNotificationRuleRecipientsConfig implements NotificationRuleRecipientsConfig { @NotEmpty private List targets; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java index df13e60e93..fa5a10f983 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java @@ -27,8 +27,8 @@ import java.util.UUID; @Schema(description = "Escalated notification rule recipients configuration") @Data -@EqualsAndHashCode(callSuper = true) -public class EscalatedNotificationRuleRecipientsConfig extends NotificationRuleRecipientsConfig { +@EqualsAndHashCode +public class EscalatedNotificationRuleRecipientsConfig implements NotificationRuleRecipientsConfig { @NotEmpty private Map> escalationTable; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java index ba968841c8..0b383a6d22 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java @@ -16,13 +16,11 @@ package org.thingsboard.server.common.data.notification.rule; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import java.io.Serializable; @@ -48,7 +46,6 @@ import java.util.UUID; @DiscriminatorMapping(value = "TASK_PROCESSING_FAILURE", schema = DefaultNotificationRuleRecipientsConfig.TaskProcessingFailureRecipientsConfig.class), @DiscriminatorMapping(value = "RESOURCES_SHORTAGE", schema = DefaultNotificationRuleRecipientsConfig.ResourceShortageRecipientsConfig.class) }) -@JsonIgnoreProperties @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType", include = JsonTypeInfo.As.EXISTING_PROPERTY) @JsonSubTypes({ @Type(name = "ALARM", value = EscalatedNotificationRuleRecipientsConfig.class), @@ -66,12 +63,11 @@ import java.util.UUID; @Type(name = "TASK_PROCESSING_FAILURE", value = DefaultNotificationRuleRecipientsConfig.TaskProcessingFailureRecipientsConfig.class), @Type(name = "RESOURCES_SHORTAGE", value = DefaultNotificationRuleRecipientsConfig.ResourceShortageRecipientsConfig.class) }) -@Data -public abstract class NotificationRuleRecipientsConfig implements Serializable { +public interface NotificationRuleRecipientsConfig extends Serializable { - public abstract NotificationRuleTriggerType getTriggerType(); + NotificationRuleTriggerType getTriggerType(); @JsonIgnore - public abstract Map> getTargetsTable(); + Map> getTargetsTable(); }