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