committed by
GitHub
42 changed files with 2190 additions and 59 deletions
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import jakarta.validation.Valid; |
|||
import lombok.Data; |
|||
|
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class DashboardConfig { |
|||
|
|||
private Map<UUID, @Valid EntityAlias> entityAliases; |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import jakarta.validation.Valid; |
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.dashboard.filter.DashboardAliasFilter; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class EntityAlias { |
|||
|
|||
@NotNull |
|||
private UUID id; |
|||
|
|||
@NotBlank |
|||
private String alias; |
|||
|
|||
@NotNull |
|||
@Valid |
|||
private DashboardAliasFilter filter; |
|||
|
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
include = JsonTypeInfo.As.PROPERTY, |
|||
property = "type") |
|||
@JsonSubTypes({ |
|||
@JsonSubTypes.Type(value = DashboardSingleEntityFilter.class, name = "singleEntity"), |
|||
@JsonSubTypes.Type(value = DashboardEntityListFilter.class, name = "entityList"), |
|||
@JsonSubTypes.Type(value = DashboardEntityNameFilter.class, name = "entityName"), |
|||
@JsonSubTypes.Type(value = DashboardEntityTypeFilter.class, name = "entityType"), |
|||
@JsonSubTypes.Type(value = DashboardStateEntityFilter.class, name = "stateEntity"), |
|||
@JsonSubTypes.Type(value = DashboardAssetTypeFilter.class, name = "assetType"), |
|||
@JsonSubTypes.Type(value = DashboardDeviceTypeFilter.class, name = "deviceType"), |
|||
@JsonSubTypes.Type(value = DashboardEdgeTypeFilter.class, name = "edgeType"), |
|||
@JsonSubTypes.Type(value = DashboardEntityViewTypeFilter.class, name = "entityViewType"), |
|||
@JsonSubTypes.Type(value = DashboardApiUsageStateFilter.class, name = "apiUsageState"), |
|||
@JsonSubTypes.Type(value = DashboardRelationsQueryFilter.class, name = "relationsQuery"), |
|||
@JsonSubTypes.Type(value = DashboardAssetSearchQueryFilter.class, name = "assetSearchQuery"), |
|||
@JsonSubTypes.Type(value = DashboardDeviceSearchQueryFilter.class, name = "deviceSearchQuery"), |
|||
@JsonSubTypes.Type(value = DashboardEntityViewSearchQueryFilter.class, name = "entityViewSearchQuery"), |
|||
@JsonSubTypes.Type(value = DashboardEdgeSearchQueryFilter.class, name = "edgeSearchQuery") |
|||
}) |
|||
public abstract class DashboardAliasFilter { |
|||
|
|||
private boolean resolveMultiple; |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardApiUsageStateFilter extends DashboardAliasFilter { |
|||
|
|||
private CustomerId customerId; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardAssetSearchQueryFilter extends DashboardEntitySearchQueryFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> assetTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardAssetTypeFilter extends DashboardAliasFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> assetTypes; |
|||
|
|||
private String assetNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardDeviceSearchQueryFilter extends DashboardEntitySearchQueryFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> deviceTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardDeviceTypeFilter extends DashboardAliasFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> deviceTypes; |
|||
|
|||
private String deviceNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEdgeSearchQueryFilter extends DashboardEntitySearchQueryFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> edgeTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEdgeTypeFilter extends DashboardAliasFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> edgeTypes; |
|||
|
|||
private String edgeNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEntityListFilter extends DashboardAliasFilter { |
|||
|
|||
@NotNull |
|||
private EntityType entityType; |
|||
|
|||
@NotEmpty |
|||
private List<@NotNull UUID> entityList; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEntityNameFilter extends DashboardAliasFilter { |
|||
|
|||
@NotNull |
|||
private EntityType entityType; |
|||
|
|||
@NotBlank |
|||
private String entityNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotNull; |
|||
import jakarta.validation.constraints.PositiveOrZero; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.query.AliasEntityId; |
|||
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public abstract class DashboardEntitySearchQueryFilter extends DashboardAliasFilter implements DashboardStatefulRootFilter { |
|||
|
|||
private AliasEntityId rootEntity; |
|||
|
|||
private boolean rootStateEntity; |
|||
|
|||
private String stateEntityParamName; |
|||
|
|||
private AliasEntityId defaultStateEntity; |
|||
|
|||
private String relationType; |
|||
|
|||
@NotNull |
|||
private EntitySearchDirection direction; |
|||
|
|||
@PositiveOrZero |
|||
private int maxLevel; |
|||
|
|||
private boolean fetchLastLevelOnly; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEntityTypeFilter extends DashboardAliasFilter { |
|||
|
|||
@NotNull |
|||
private EntityType entityType; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEntityViewSearchQueryFilter extends DashboardEntitySearchQueryFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> entityViewTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardEntityViewTypeFilter extends DashboardAliasFilter { |
|||
|
|||
@NotEmpty |
|||
private List<@NotBlank String> entityViewTypes; |
|||
|
|||
private String entityViewNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.Valid; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import jakarta.validation.constraints.PositiveOrZero; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.query.AliasEntityId; |
|||
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
|||
import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardRelationsQueryFilter extends DashboardAliasFilter implements DashboardStatefulRootFilter { |
|||
|
|||
private AliasEntityId rootEntity; |
|||
|
|||
private boolean rootStateEntity; |
|||
|
|||
private String stateEntityParamName; |
|||
|
|||
private AliasEntityId defaultStateEntity; |
|||
|
|||
@NotNull |
|||
private EntitySearchDirection direction; |
|||
|
|||
@PositiveOrZero |
|||
private int maxLevel; |
|||
|
|||
private boolean fetchLastLevelOnly; |
|||
|
|||
@Valid |
|||
private List<RelationEntityTypeFilter> filters; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.query.AliasEntityId; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardSingleEntityFilter extends DashboardAliasFilter { |
|||
|
|||
@NotNull |
|||
private AliasEntityId singleEntity; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.query.AliasEntityId; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class DashboardStateEntityFilter extends DashboardAliasFilter { |
|||
|
|||
private String stateEntityParamName; |
|||
|
|||
private AliasEntityId defaultStateEntity; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.dashboard.filter; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import jakarta.validation.constraints.AssertTrue; |
|||
import org.thingsboard.server.common.data.query.AliasEntityId; |
|||
|
|||
public interface DashboardStatefulRootFilter { |
|||
|
|||
AliasEntityId getRootEntity(); |
|||
|
|||
boolean isRootStateEntity(); |
|||
|
|||
@AssertTrue(message = "must include 'rootEntity' when 'rootStateEntity' is false") |
|||
@JsonIgnore |
|||
default boolean isValidRootEntity() { |
|||
return isRootStateEntity() || getRootEntity() != null; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.test.context.bean.override.mockito.MockitoBean; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import static org.mockito.BDDMockito.willReturn; |
|||
|
|||
@SpringBootTest(classes = DashboardDataValidator.class) |
|||
public abstract class AbstractDashboardDataValidatorTest { |
|||
|
|||
protected static final String ALIAS_UUID = "a1ddb8fa-90ff-5598-e7f2-e254194d055d"; |
|||
|
|||
@MockitoBean |
|||
protected TenantService tenantService; |
|||
@Autowired |
|||
protected DashboardDataValidator validator; |
|||
|
|||
protected final TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
willReturn(true).given(tenantService).tenantExists(tenantId); |
|||
} |
|||
|
|||
protected Dashboard dashboardWith(JsonNode configuration) { |
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle("test dashboard"); |
|||
dashboard.setTenantId(tenantId); |
|||
dashboard.setConfiguration(configuration); |
|||
return dashboard; |
|||
} |
|||
|
|||
protected Dashboard filterAlias(String aliasName, String filterJson) { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": { |
|||
"id": "%s", |
|||
"alias": "%s", |
|||
"filter": %s |
|||
} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID, aliasName, filterJson); |
|||
return dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
} |
|||
|
|||
protected void validate(Dashboard dashboard) { |
|||
validator.validateDataImpl(tenantId, dashboard); |
|||
} |
|||
|
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator; |
|||
|
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.boot.test.mock.mockito.SpyBean; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import static org.mockito.BDDMockito.willReturn; |
|||
import static org.mockito.Mockito.verify; |
|||
|
|||
@SpringBootTest(classes = DashboardDataValidator.class) |
|||
class DashboardDataValidatorTest { |
|||
|
|||
@MockBean |
|||
TenantService tenantService; |
|||
@SpyBean |
|||
DashboardDataValidator validator; |
|||
TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); |
|||
|
|||
@BeforeEach |
|||
void setUp() { |
|||
willReturn(true).given(tenantService).tenantExists(tenantId); |
|||
} |
|||
|
|||
@Test |
|||
void testValidateNameInvocation() { |
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle("flight control"); |
|||
dashboard.setTenantId(tenantId); |
|||
|
|||
validator.validateDataImpl(tenantId, dashboard); |
|||
verify(validator).validateString("Dashboard title", dashboard.getTitle()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
|
|||
class DashboardApiUsageStateFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptApiUsageStateFilterWithoutCustomerId() { |
|||
Dashboard dashboard = filterAlias("Tenant Usage", """ |
|||
{"type": "apiUsageState"}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptApiUsageStateFilterWithCustomerId() { |
|||
Dashboard dashboard = filterAlias("Customer Usage", """ |
|||
{"type": "apiUsageState", "customerId": {"entityType": "CUSTOMER", "id": "%s"}}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardAssetSearchQueryFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidAssetSearchQueryFilter() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{ |
|||
"type": "assetSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"assetTypes": ["thermostat"] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectAssetSearchQueryFilterWithEmptyAssetTypes() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{ |
|||
"type": "assetSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"assetTypes": [] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Assets' field 'assetTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectAssetSearchQueryFilterWithBlankAssetTypeElement() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{ |
|||
"type": "assetSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"assetTypes": ["thermostat", " "] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Assets' field 'assetTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardAssetTypeFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidAssetTypeFilter() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{"type": "assetType", "assetTypes": ["thermostat", "valve"]}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectAssetTypeFilterWithEmptyAssetTypes() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{"type": "assetType", "assetTypes": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Assets' field 'assetTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectAssetTypeFilterWithBlankAssetTypeElement() { |
|||
Dashboard dashboard = filterAlias("Assets", """ |
|||
{"type": "assetType", "assetTypes": ["thermostat", " "]}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Assets' field 'assetTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,314 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Nested; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardConfigurationStructureTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Nested |
|||
class Title { |
|||
|
|||
@Test |
|||
void shouldAcceptValidTitle() { |
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle("flight control"); |
|||
dashboard.setTenantId(tenantId); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectBlankTitle() { |
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle(" "); |
|||
dashboard.setTenantId(tenantId); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard title should be specified!"); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Nested |
|||
class Tenant { |
|||
|
|||
@Test |
|||
void shouldRejectDashboardReferencingNonExistentTenant() { |
|||
TenantId unknownTenantId = TenantId.fromUUID(UUID.fromString("11111111-1111-1111-1111-111111111111")); |
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle("flight control"); |
|||
dashboard.setTenantId(unknownTenantId); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard is referencing to non-existent tenant!"); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Nested |
|||
class ConfigurationShape { |
|||
|
|||
@Test |
|||
void shouldAcceptDashboardWithoutConfiguration() { |
|||
Dashboard dashboard = dashboardWith(null); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptNonObjectConfiguration() { |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode("\"just a string\"")); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptConfigurationWithoutEntityAliases() { |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode("{\"widgets\": {}}")); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptEmptyEntityAliasesMap() { |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode("{\"entityAliases\": {}}")); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptNullEntityAliases() { |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode("{\"entityAliases\": null}")); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityAliasesAsArray() { |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode("{\"entityAliases\": []}")); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Nested |
|||
class EntityAliasShape { |
|||
|
|||
@Test |
|||
void shouldAcceptWellFormedEntityAlias() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityType", "entityType": "DEVICE"}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectBlankAliasDisplayName() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": { |
|||
"id": "%s", |
|||
"alias": "", |
|||
"filter": {"type": "entityType", "entityType": "DEVICE"} |
|||
} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias '" + ALIAS_UUID + "' field 'alias' must not be blank"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectKeyThatIsNotUuid() { |
|||
String json = """ |
|||
{"entityAliases": {"not-a-uuid": {"id": "%s", "alias": "X", "filter": {"type": "entityType", "entityType": "DEVICE"}}}}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectAliasValueThatIsNotObject() { |
|||
String json = """ |
|||
{"entityAliases": {"%s": "not an object"}}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectMissingIdField() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"alias": "Devices", "filter": {"type": "entityType", "entityType": "DEVICE"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'id' must not be null"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectNonTextualIdField() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": 42, "alias": "Devices", "filter": {"type": "entityType", "entityType": "DEVICE"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectIdThatIsNotUuid() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "not-a-uuid", "alias": "Devices", "filter": {"type": "entityType", "entityType": "DEVICE"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectIdNotMatchingKey() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "11111111-1111-1111-1111-111111111111", "alias": "Devices", "filter": {"type": "entityType", "entityType": "DEVICE"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' has 'id' that does not match its key!"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectMissingAliasField() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "%s", "filter": {"type": "entityType", "entityType": "DEVICE"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias '" + ALIAS_UUID + "' field 'alias' must not be blank"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectMissingFilterField() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "%s", "alias": "Devices"} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'filter' must not be null"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectFilterWithoutType() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "%s", "alias": "Devices", "filter": {}} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectFilterWithUnknownType() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "%s", "alias": "Devices", "filter": {"type": "bogus"}} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectNonObjectFilterField() { |
|||
String json = """ |
|||
{ |
|||
"entityAliases": { |
|||
"%s": {"id": "%s", "alias": "Devices", "filter": "not an object"} |
|||
} |
|||
}""".formatted(ALIAS_UUID, ALIAS_UUID); |
|||
Dashboard dashboard = dashboardWith(JacksonUtil.toJsonNode(json)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessageStartingWith("Dashboard configuration has invalid structure:"); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardDeviceSearchQueryFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidDeviceSearchQueryFilter() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{ |
|||
"type": "deviceSearchQuery", |
|||
"rootEntity": {"entityType": "ASSET", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"deviceTypes": ["thermostat"] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectDeviceSearchQueryFilterWithEmptyDeviceTypes() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{ |
|||
"type": "deviceSearchQuery", |
|||
"rootEntity": {"entityType": "ASSET", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"deviceTypes": [] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'deviceTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectDeviceSearchQueryFilterWithBlankDeviceTypeElement() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{ |
|||
"type": "deviceSearchQuery", |
|||
"rootEntity": {"entityType": "ASSET", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"deviceTypes": ["thermostat", " "] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'deviceTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardDeviceTypeFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidDeviceTypeFilter() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "deviceType", "deviceTypes": ["thermostat", "valve"]}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectDeviceTypeFilterWithEmptyDeviceTypes() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "deviceType", "deviceTypes": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'deviceTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectDeviceTypeFilterWithBlankDeviceTypeElement() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "deviceType", "deviceTypes": ["thermostat", " "]}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'deviceTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEdgeSearchQueryFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEdgeSearchQueryFilter() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{ |
|||
"type": "edgeSearchQuery", |
|||
"rootEntity": {"entityType": "TENANT", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"edgeTypes": ["gateway"] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEdgeSearchQueryFilterWithEmptyEdgeTypes() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{ |
|||
"type": "edgeSearchQuery", |
|||
"rootEntity": {"entityType": "TENANT", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"edgeTypes": [] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Edges' field 'edgeTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEdgeSearchQueryFilterWithBlankEdgeTypeElement() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{ |
|||
"type": "edgeSearchQuery", |
|||
"rootEntity": {"entityType": "TENANT", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"edgeTypes": ["gateway", " "] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Edges' field 'edgeTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEdgeTypeFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEdgeTypeFilter() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{"type": "edgeType", "edgeTypes": ["gateway"]}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEdgeTypeFilterWithEmptyEdgeTypes() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{"type": "edgeType", "edgeTypes": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Edges' field 'edgeTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEdgeTypeFilterWithBlankEdgeTypeElement() { |
|||
Dashboard dashboard = filterAlias("Edges", """ |
|||
{"type": "edgeType", "edgeTypes": ["gateway", " "]}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Edges' field 'edgeTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEntityListFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEntityListFilter() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityList", "entityType": "DEVICE", "entityList": ["11111111-1111-1111-1111-111111111111"]}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityListFilterWithEmptyEntityList() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityList", "entityType": "DEVICE", "entityList": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'entityList' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityListFilterWithoutEntityType() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityList", "entityList": ["11111111-1111-1111-1111-111111111111"]}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'entityType' must not be null"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEntityNameFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEntityNameFilter() { |
|||
Dashboard dashboard = filterAlias("Sensors", """ |
|||
{"type": "entityName", "entityType": "DEVICE", "entityNameFilter": "sensor"}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityNameFilterWithoutEntityType() { |
|||
Dashboard dashboard = filterAlias("Sensors", """ |
|||
{"type": "entityName", "entityNameFilter": "sensor"}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Sensors' field 'entityType' must not be null"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityNameFilterWithBlankEntityNameFilter() { |
|||
Dashboard dashboard = filterAlias("Sensors", """ |
|||
{"type": "entityName", "entityType": "DEVICE", "entityNameFilter": " "}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Sensors' field 'entityNameFilter' must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEntityTypeFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEntityTypeFilter() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityType", "entityType": "DEVICE"}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityTypeFilterWithoutEntityType() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityType"}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'entityType' must not be null"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEntityViewSearchQueryFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEntityViewSearchQueryFilter() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{ |
|||
"type": "entityViewSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"entityViewTypes": ["summary"] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityViewSearchQueryFilterWithEmptyEntityViewTypes() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{ |
|||
"type": "entityViewSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"entityViewTypes": [] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Views' field 'entityViewTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityViewSearchQueryFilterWithBlankEntityViewTypeElement() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{ |
|||
"type": "entityViewSearchQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"entityViewTypes": ["summary", " "] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Views' field 'entityViewTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardEntityViewTypeFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidEntityViewTypeFilter() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{"type": "entityViewType", "entityViewTypes": ["summary"]}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityViewTypeFilterWithEmptyEntityViewTypes() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{"type": "entityViewType", "entityViewTypes": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Views' field 'entityViewTypes' must not be empty"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectEntityViewTypeFilterWithBlankEntityViewTypeElement() { |
|||
Dashboard dashboard = filterAlias("Views", """ |
|||
{"type": "entityViewType", "entityViewTypes": ["summary", " "]}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Views' field 'entityViewTypes' element at index 1 must not be blank"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardRelationsQueryFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidRelationsQueryFilter() { |
|||
Dashboard dashboard = filterAlias("Related", """ |
|||
{ |
|||
"type": "relationsQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 1, |
|||
"filters": [] |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptRelationsQueryFilterWithStateDrivenRoot() { |
|||
Dashboard dashboard = filterAlias("Related", """ |
|||
{ |
|||
"type": "relationsQuery", |
|||
"rootStateEntity": true, |
|||
"direction": "FROM", |
|||
"maxLevel": 1 |
|||
}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptRelationsQueryFilterWithZeroMaxLevel() { |
|||
// maxLevel = 0 means "unlimited" in the UI
|
|||
Dashboard dashboard = filterAlias("Related", """ |
|||
{ |
|||
"type": "relationsQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"direction": "FROM", |
|||
"maxLevel": 0 |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectRelationsQueryFilterWithoutDirection() { |
|||
Dashboard dashboard = filterAlias("Related", """ |
|||
{ |
|||
"type": "relationsQuery", |
|||
"rootEntity": {"entityType": "DEVICE", "id": "%s"}, |
|||
"maxLevel": 1 |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Related' field 'direction' must not be null"); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectRelationsQueryFilterWithoutRootEntityWhenStateDrivenIsFalse() { |
|||
Dashboard dashboard = filterAlias("Related", """ |
|||
{ |
|||
"type": "relationsQuery", |
|||
"direction": "FROM", |
|||
"maxLevel": 1 |
|||
}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Related' must include 'rootEntity' when 'rootStateEntity' is false"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardSingleEntityFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptValidSingleEntityFilter() { |
|||
Dashboard dashboard = filterAlias("Sensor", """ |
|||
{"type": "singleEntity", "singleEntity": {"entityType": "DEVICE", "id": "%s"}}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptSingleEntityFilterWithAliasEntity() { |
|||
Dashboard dashboard = filterAlias("Current Tenant", """ |
|||
{"type": "singleEntity", "singleEntity": {"entityType": "CURRENT_TENANT"}}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldRejectSingleEntityFilterWithoutSingleEntity() { |
|||
Dashboard dashboard = filterAlias("Sensor", """ |
|||
{"type": "singleEntity"}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Sensor' field 'singleEntity' must not be null"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
|
|||
class DashboardStateEntityFilterValidationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAcceptStateEntityFilterWithoutAnyFields() { |
|||
Dashboard dashboard = filterAlias("Selected Device", """ |
|||
{"type": "stateEntity"}"""); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
@Test |
|||
void shouldAcceptStateEntityFilterWithBothFieldsSet() { |
|||
Dashboard dashboard = filterAlias("Selected Device", """ |
|||
{ |
|||
"type": "stateEntity", |
|||
"stateEntityParamName": "deviceId", |
|||
"defaultStateEntity": {"entityType": "DEVICE", "id": "%s"} |
|||
}""".formatted(ALIAS_UUID)); |
|||
|
|||
assertThatCode(() -> validate(dashboard)).doesNotThrowAnyException(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/** |
|||
* Copyright © 2016-2026 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.validator.dashboard; |
|||
|
|||
import org.thingsboard.server.dao.service.validator.AbstractDashboardDataValidatorTest; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
|
|||
class DashboardViolationAggregationTest extends AbstractDashboardDataValidatorTest { |
|||
|
|||
@Test |
|||
void shouldAggregateMultipleViolations() { |
|||
Dashboard dashboard = filterAlias("Devices", """ |
|||
{"type": "entityList", "entityList": []}"""); |
|||
|
|||
assertThatThrownBy(() -> validate(dashboard)) |
|||
.isInstanceOf(DataValidationException.class) |
|||
.hasMessage("Dashboard validation error: alias 'Devices' field 'entityList' must not be empty, alias 'Devices' field 'entityType' must not be null"); |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue