Browse Source
- Full entity stack: IotHubInstalledItem with ID, descriptors, JPA entity, DAO (extends JpaAbstractDao), service - Controller endpoints: install, list (paginated), list infos, get by itemId, delete - Install flow saves descriptor with entity IDs, delete cascades to installed entities - Frontend installed items page with search, pagination, type chips, action buttons - Browse page shows "Installed" label on cards, detail dialog shows "Open details" for installed items - Installed item infos fetched on browse page init to track installation statepull/15347/head
40 changed files with 1763 additions and 69 deletions
@ -0,0 +1,40 @@ |
|||
/** |
|||
* 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.service.iot_hub; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemDescriptor; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class InstallItemVersionResult { |
|||
|
|||
private boolean success; |
|||
private String errorMessage; |
|||
private IotHubInstalledItemDescriptor descriptor; |
|||
|
|||
public static InstallItemVersionResult success(IotHubInstalledItemDescriptor descriptor) { |
|||
return new InstallItemVersionResult(true, null, descriptor); |
|||
} |
|||
|
|||
public static InstallItemVersionResult error(String errorMessage) { |
|||
return new InstallItemVersionResult(false, errorMessage, null); |
|||
} |
|||
|
|||
} |
|||
@ -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.id; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public class IotHubInstalledItemId extends UUIDBased { |
|||
|
|||
@JsonCreator |
|||
public IotHubInstalledItemId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
public static IotHubInstalledItemId fromString(String id) { |
|||
return new IotHubInstalledItemId(UUID.fromString(id)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|||
|
|||
@Data |
|||
public class CalculatedFieldInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
private CalculatedFieldId calculatedFieldId; |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.DashboardId; |
|||
|
|||
@Data |
|||
public class DashboardInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
private DashboardId dashboardId; |
|||
|
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class DeviceInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
} |
|||
@ -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.common.data.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.BaseData; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.id.IotHubInstalledItemId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class IotHubInstalledItem extends BaseData<IotHubInstalledItemId> implements HasTenantId { |
|||
|
|||
private TenantId tenantId; |
|||
private UUID itemId; |
|||
private UUID itemVersionId; |
|||
private String itemName; |
|||
private String itemType; |
|||
private String version; |
|||
private IotHubInstalledItemDescriptor descriptor; |
|||
|
|||
public IotHubInstalledItem() { |
|||
super(); |
|||
} |
|||
|
|||
public IotHubInstalledItem(IotHubInstalledItemId id) { |
|||
super(id); |
|||
} |
|||
|
|||
} |
|||
@ -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.common.data.iot_hub; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
property = "type") |
|||
@JsonSubTypes({ |
|||
@Type(name = "WIDGET", value = WidgetInstalledItemDescriptor.class), |
|||
@Type(name = "DASHBOARD", value = DashboardInstalledItemDescriptor.class), |
|||
@Type(name = "CALCULATED_FIELD", value = CalculatedFieldInstalledItemDescriptor.class), |
|||
@Type(name = "RULE_CHAIN", value = RuleChainInstalledItemDescriptor.class), |
|||
@Type(name = "DEVICE", value = DeviceInstalledItemDescriptor.class) |
|||
}) |
|||
public interface IotHubInstalledItemDescriptor extends Serializable { |
|||
|
|||
} |
|||
@ -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.iot_hub; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class IotHubInstalledItemInfo { |
|||
|
|||
private UUID itemId; |
|||
private UUID itemVersionId; |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
|
|||
@Data |
|||
public class RuleChainInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
private RuleChainId ruleChainId; |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.WidgetTypeId; |
|||
|
|||
@Data |
|||
public class WidgetInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
private WidgetTypeId widgetTypeId; |
|||
|
|||
} |
|||
@ -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.dao.iot_hub; |
|||
|
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItem; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.dao.Dao; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
public interface IotHubInstalledItemDao extends Dao<IotHubInstalledItem> { |
|||
|
|||
Optional<IotHubInstalledItem> findByTenantIdAndItemId(TenantId tenantId, UUID itemId); |
|||
|
|||
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
List<IotHubInstalledItemInfo> findInstalledItemInfosByTenantId(TenantId tenantId); |
|||
|
|||
boolean deleteByTenantIdAndItemId(TenantId tenantId, UUID itemId); |
|||
|
|||
void deleteByTenantId(TenantId tenantId); |
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItem; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
public interface IotHubInstalledItemService { |
|||
|
|||
IotHubInstalledItem save(TenantId tenantId, IotHubInstalledItem item); |
|||
|
|||
Optional<IotHubInstalledItem> findByTenantIdAndItemId(TenantId tenantId, UUID itemId); |
|||
|
|||
PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink); |
|||
|
|||
List<IotHubInstalledItemInfo> findInstalledItemInfosByTenantId(TenantId tenantId); |
|||
|
|||
boolean deleteByTenantIdAndItemId(TenantId tenantId, UUID itemId); |
|||
|
|||
void deleteByTenantId(TenantId tenantId); |
|||
|
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
/** |
|||
* 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.iot_hub; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItem; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
@Service |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
class IotHubInstalledItemServiceImpl implements IotHubInstalledItemService { |
|||
|
|||
private final IotHubInstalledItemDao iotHubInstalledItemDao; |
|||
|
|||
@Override |
|||
public IotHubInstalledItem save(TenantId tenantId, IotHubInstalledItem item) { |
|||
log.debug("[{}] Saving IoT Hub installed item: {}", tenantId, item); |
|||
return iotHubInstalledItemDao.save(tenantId, item); |
|||
} |
|||
|
|||
@Override |
|||
public Optional<IotHubInstalledItem> findByTenantIdAndItemId(TenantId tenantId, UUID itemId) { |
|||
return iotHubInstalledItemDao.findByTenantIdAndItemId(tenantId, itemId); |
|||
} |
|||
|
|||
@Override |
|||
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink) { |
|||
return iotHubInstalledItemDao.findByTenantId(tenantId, pageLink); |
|||
} |
|||
|
|||
@Override |
|||
public List<IotHubInstalledItemInfo> findInstalledItemInfosByTenantId(TenantId tenantId) { |
|||
return iotHubInstalledItemDao.findInstalledItemInfosByTenantId(tenantId); |
|||
} |
|||
|
|||
@Override |
|||
public boolean deleteByTenantIdAndItemId(TenantId tenantId, UUID itemId) { |
|||
return iotHubInstalledItemDao.deleteByTenantIdAndItemId(tenantId, itemId); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteByTenantId(TenantId tenantId) { |
|||
iotHubInstalledItemDao.deleteByTenantId(tenantId); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
/** |
|||
* 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.model.sql; |
|||
|
|||
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType; |
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.Table; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
import org.hibernate.annotations.Type; |
|||
import org.hibernate.proxy.HibernateProxy; |
|||
import org.thingsboard.server.common.data.id.IotHubInstalledItemId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItem; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemDescriptor; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
|
|||
import java.util.Objects; |
|||
import java.util.UUID; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@ToString |
|||
@Entity |
|||
@Table(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_TABLE_NAME) |
|||
public class IotHubInstalledItemEntity extends BaseSqlEntity<IotHubInstalledItem> { |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_TENANT_ID_COLUMN, nullable = false, columnDefinition = "UUID") |
|||
private UUID tenantId; |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_ITEM_ID_COLUMN, nullable = false, columnDefinition = "UUID") |
|||
private UUID itemId; |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_ITEM_VERSION_ID_COLUMN, nullable = false, columnDefinition = "UUID") |
|||
private UUID itemVersionId; |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_ITEM_NAME_COLUMN, nullable = false) |
|||
private String itemName; |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_ITEM_TYPE_COLUMN, nullable = false) |
|||
private String itemType; |
|||
|
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_VERSION_COLUMN, nullable = false) |
|||
private String version; |
|||
|
|||
@Type(JsonBinaryType.class) |
|||
@Column(name = ModelConstants.IOT_HUB_INSTALLED_ITEM_DESCRIPTOR_COLUMN, nullable = false, columnDefinition = "JSONB") |
|||
private IotHubInstalledItemDescriptor descriptor; |
|||
|
|||
public IotHubInstalledItemEntity() { |
|||
} |
|||
|
|||
public IotHubInstalledItemEntity(IotHubInstalledItem item) { |
|||
super(item); |
|||
tenantId = getTenantUuid(item.getTenantId()); |
|||
itemId = item.getItemId(); |
|||
itemVersionId = item.getItemVersionId(); |
|||
itemName = item.getItemName(); |
|||
itemType = item.getItemType(); |
|||
version = item.getVersion(); |
|||
descriptor = item.getDescriptor(); |
|||
} |
|||
|
|||
@Override |
|||
public IotHubInstalledItem toData() { |
|||
var item = new IotHubInstalledItem(new IotHubInstalledItemId(id)); |
|||
item.setCreatedTime(createdTime); |
|||
item.setTenantId(TenantId.fromUUID(tenantId)); |
|||
item.setItemId(itemId); |
|||
item.setItemVersionId(itemVersionId); |
|||
item.setItemName(itemName); |
|||
item.setItemType(itemType); |
|||
item.setVersion(version); |
|||
item.setDescriptor(descriptor); |
|||
return item; |
|||
} |
|||
|
|||
@Override |
|||
public final boolean equals(Object o) { |
|||
if (this == o) return true; |
|||
if (o == null) return false; |
|||
Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass(); |
|||
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass(); |
|||
if (thisEffectiveClass != oEffectiveClass) return false; |
|||
IotHubInstalledItemEntity that = (IotHubInstalledItemEntity) o; |
|||
return getId() != null && Objects.equals(getId(), that.getId()); |
|||
} |
|||
|
|||
@Override |
|||
public final int hashCode() { |
|||
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
/** |
|||
* 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.sql.iot_hub; |
|||
|
|||
import org.springframework.data.domain.Page; |
|||
import org.springframework.data.domain.Pageable; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.data.jpa.repository.Modifying; |
|||
import org.springframework.data.jpa.repository.Query; |
|||
import org.springframework.data.repository.query.Param; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo; |
|||
import org.thingsboard.server.dao.model.sql.IotHubInstalledItemEntity; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
interface IotHubInstalledItemRepository extends JpaRepository<IotHubInstalledItemEntity, UUID> { |
|||
|
|||
Optional<IotHubInstalledItemEntity> findByTenantIdAndItemId(UUID tenantId, UUID itemId); |
|||
|
|||
@Query("SELECT new org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo(item.itemId, item.itemVersionId) " + |
|||
"FROM IotHubInstalledItemEntity item WHERE item.tenantId = :tenantId") |
|||
List<IotHubInstalledItemInfo> findInstalledItemInfosByTenantId(@Param("tenantId") UUID tenantId); |
|||
|
|||
@Query(""" |
|||
SELECT item FROM IotHubInstalledItemEntity item |
|||
WHERE item.tenantId = :tenantId |
|||
AND (:textSearch IS NULL |
|||
OR ilike(item.itemName, CONCAT('%', :textSearch, '%')) = true |
|||
OR ilike(item.itemType, CONCAT('%', :textSearch, '%')) = true |
|||
OR ilike(item.version, CONCAT('%', :textSearch, '%')) = true) |
|||
""") |
|||
Page<IotHubInstalledItemEntity> findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); |
|||
|
|||
@Transactional |
|||
@Modifying |
|||
@Query("DELETE FROM IotHubInstalledItemEntity item WHERE item.tenantId = :tenantId AND item.itemId = :itemId") |
|||
int deleteByTenantIdAndItemId(@Param("tenantId") UUID tenantId, @Param("itemId") UUID itemId); |
|||
|
|||
@Transactional |
|||
@Modifying |
|||
@Query(value = "DELETE FROM iot_hub_installed_item WHERE tenant_id = :tenantId", nativeQuery = true) |
|||
void deleteByTenantId(@Param("tenantId") UUID tenantId); |
|||
|
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
/** |
|||
* 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.sql.iot_hub; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.data.domain.PageRequest; |
|||
import org.springframework.data.domain.Sort; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItem; |
|||
import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemInfo; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.common.data.page.SortOrder; |
|||
import org.thingsboard.server.dao.DaoUtil; |
|||
import org.thingsboard.server.dao.iot_hub.IotHubInstalledItemDao; |
|||
import org.thingsboard.server.dao.model.sql.IotHubInstalledItemEntity; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDao; |
|||
import org.thingsboard.server.dao.util.SqlDao; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
@SqlDao |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
class JpaIotHubInstalledItemDao extends JpaAbstractDao<IotHubInstalledItemEntity, IotHubInstalledItem> implements IotHubInstalledItemDao { |
|||
|
|||
private final IotHubInstalledItemRepository repository; |
|||
|
|||
@Override |
|||
public Optional<IotHubInstalledItem> findByTenantIdAndItemId(TenantId tenantId, UUID itemId) { |
|||
return repository.findByTenantIdAndItemId(tenantId.getId(), itemId).map(DaoUtil::getData); |
|||
} |
|||
|
|||
@Override |
|||
public PageData<IotHubInstalledItem> findByTenantId(TenantId tenantId, PageLink pageLink) { |
|||
return DaoUtil.toPageData(repository.findByTenantId( |
|||
tenantId.getId(), |
|||
StringUtils.defaultIfEmpty(pageLink.getTextSearch(), null), |
|||
toPageRequest(pageLink)) |
|||
); |
|||
} |
|||
|
|||
@Override |
|||
public boolean deleteByTenantIdAndItemId(TenantId tenantId, UUID itemId) { |
|||
return repository.deleteByTenantIdAndItemId(tenantId.getId(), itemId) > 0; |
|||
} |
|||
|
|||
@Override |
|||
public List<IotHubInstalledItemInfo> findInstalledItemInfosByTenantId(TenantId tenantId) { |
|||
return repository.findInstalledItemInfosByTenantId(tenantId.getId()); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteByTenantId(TenantId tenantId) { |
|||
repository.deleteByTenantId(tenantId.getId()); |
|||
} |
|||
|
|||
private static PageRequest toPageRequest(PageLink pageLink) { |
|||
Sort sort; |
|||
SortOrder sortOrder = pageLink.getSortOrder(); |
|||
if (sortOrder == null) { |
|||
sort = Sort.by(Sort.Direction.DESC, "createdTime"); |
|||
} else { |
|||
sort = Sort.by( |
|||
Sort.Direction.fromString(sortOrder.getDirection().name()), |
|||
sortOrder.getProperty() |
|||
).and(Sort.by(Sort.Direction.ASC, "id")); |
|||
} |
|||
return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), sort); |
|||
} |
|||
|
|||
@Override |
|||
protected Class<IotHubInstalledItemEntity> getEntityClass() { |
|||
return IotHubInstalledItemEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected JpaRepository<IotHubInstalledItemEntity, UUID> getRepository() { |
|||
return repository; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<div class="tb-iot-hub-installed-items-container"> |
|||
<a class="tb-iot-hub-back-link" (click)="navigateToMarketplace()"> |
|||
<mat-icon>arrow_back</mat-icon> |
|||
{{ 'iot-hub.back-to-marketplace' | translate }} |
|||
</a> |
|||
<div class="tb-iot-hub-installed-items-header"> |
|||
<h2>{{ 'iot-hub.installed-items' | translate }}</h2> |
|||
</div> |
|||
|
|||
<div class="tb-iot-hub-installed-search-row"> |
|||
<mat-form-field appearance="outline" class="tb-iot-hub-installed-search-field"> |
|||
<mat-icon matPrefix>search</mat-icon> |
|||
<input matInput |
|||
[placeholder]="'iot-hub.search-installed-items' | translate" |
|||
[value]="textSearch" |
|||
(input)="onSearchChange($any($event.target).value)"> |
|||
@if (textSearch) { |
|||
<button matSuffix mat-icon-button (click)="onSearchChange('')"> |
|||
<mat-icon>close</mat-icon> |
|||
</button> |
|||
} |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<div class="tb-iot-hub-installed-table-container"> |
|||
@if (isLoading) { |
|||
<mat-progress-bar mode="indeterminate"></mat-progress-bar> |
|||
} |
|||
|
|||
<table mat-table [dataSource]="dataSource" matSort> |
|||
<ng-container matColumnDef="itemName"> |
|||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-name' | translate }}</th> |
|||
<td mat-cell *matCellDef="let item">{{ item.itemName }}</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="itemType"> |
|||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-type' | translate }}</th> |
|||
<td mat-cell *matCellDef="let item"> |
|||
<span class="tb-iot-hub-type-chip" [ngClass]="getItemTypeChipClass(item.itemType)">{{ getItemTypeLabel(item.itemType) }}</span> |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="version"> |
|||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.item-version' | translate }}</th> |
|||
<td mat-cell *matCellDef="let item">{{ item.version }}</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="createdTime"> |
|||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'iot-hub.installed-date' | translate }}</th> |
|||
<td mat-cell *matCellDef="let item">{{ item.createdTime | date:'medium' }}</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="actions"> |
|||
<th mat-header-cell *matHeaderCellDef></th> |
|||
<td mat-cell *matCellDef="let item"> |
|||
<button mat-icon-button |
|||
[matTooltip]="'iot-hub.view-item-details' | translate" |
|||
matTooltipPosition="above" |
|||
(click)="viewItemDetails(item); $event.stopPropagation()"> |
|||
<mat-icon>info_outline</mat-icon> |
|||
</button> |
|||
@if (getEntityId(item)) { |
|||
<button mat-icon-button color="primary" |
|||
[matTooltip]="'iot-hub.open-installed-entity' | translate" |
|||
matTooltipPosition="above" |
|||
(click)="openEntity(item); $event.stopPropagation()"> |
|||
<mat-icon>open_in_new</mat-icon> |
|||
</button> |
|||
} |
|||
<button mat-icon-button color="warn" |
|||
[matTooltip]="'action.delete' | translate" |
|||
matTooltipPosition="above" |
|||
(click)="deleteItem(item); $event.stopPropagation()"> |
|||
<mat-icon>delete</mat-icon> |
|||
</button> |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> |
|||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> |
|||
|
|||
<tr class="mat-row" *matNoDataRow> |
|||
<td class="mat-cell" [attr.colspan]="displayedColumns.length"> |
|||
<div class="tb-iot-hub-installed-empty"> |
|||
{{ 'iot-hub.no-installed-items' | translate }} |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
|
|||
<mat-paginator |
|||
[length]="totalElements" |
|||
[pageSize]="pageSize" |
|||
[pageIndex]="pageIndex" |
|||
[pageSizeOptions]="[10, 20, 50]" |
|||
(page)="onPageChange($event)"> |
|||
</mat-paginator> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,202 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
|
|||
:host { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100%; |
|||
padding: 16px 24px; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.tb-iot-hub-installed-items-container { |
|||
max-width: 1400px; |
|||
margin: 0 auto; |
|||
width: 100%; |
|||
background: white; |
|||
padding: 24px; |
|||
border-radius: 4px; |
|||
box-shadow: 0 0 1px rgba(0, 0, 0, 0.14), 0 1px 1px rgba(0, 0, 0, 0.12); |
|||
} |
|||
|
|||
.tb-iot-hub-back-link { |
|||
display: inline-flex; |
|||
align-items: center; |
|||
gap: 4px; |
|||
font-size: 13px; |
|||
color: rgba(0, 0, 0, 0.54); |
|||
cursor: pointer; |
|||
text-decoration: none; |
|||
border-bottom: none; |
|||
margin-bottom: 12px; |
|||
|
|||
mat-icon { |
|||
font-size: 18px; |
|||
width: 18px; |
|||
height: 18px; |
|||
} |
|||
|
|||
&:hover { |
|||
color: rgba(0, 0, 0, 0.87); |
|||
text-decoration: none; |
|||
border-bottom: none; |
|||
} |
|||
} |
|||
|
|||
.tb-iot-hub-installed-items-header { |
|||
margin-bottom: 24px; |
|||
|
|||
h2 { |
|||
font-size: 28px; |
|||
font-weight: 700; |
|||
margin: 0; |
|||
color: rgba(0, 0, 0, 0.87); |
|||
} |
|||
} |
|||
|
|||
.tb-iot-hub-installed-search-row { |
|||
display: flex; |
|||
gap: 12px; |
|||
margin-bottom: 16px; |
|||
align-items: center; |
|||
} |
|||
|
|||
.tb-iot-hub-installed-search-field { |
|||
flex: 1; |
|||
|
|||
::ng-deep { |
|||
.mat-mdc-text-field-wrapper { |
|||
height: 42px; |
|||
padding: 0 12px; |
|||
} |
|||
|
|||
.mat-mdc-form-field-flex { |
|||
height: 42px; |
|||
align-items: center; |
|||
} |
|||
|
|||
.mdc-notched-outline__leading, |
|||
.mdc-notched-outline__notch, |
|||
.mdc-notched-outline__trailing { |
|||
border-color: #d5dbe3; |
|||
} |
|||
|
|||
.mdc-notched-outline__leading { |
|||
border-radius: 8px 0 0 8px; |
|||
} |
|||
|
|||
.mdc-notched-outline__trailing { |
|||
border-radius: 0 8px 8px 0; |
|||
} |
|||
|
|||
.mat-mdc-form-field-infix { |
|||
padding-top: 0; |
|||
padding-bottom: 0; |
|||
min-height: unset; |
|||
} |
|||
|
|||
.mdc-notched-outline__notch { |
|||
display: none; |
|||
} |
|||
|
|||
.mat-mdc-floating-label { |
|||
display: none; |
|||
} |
|||
|
|||
.mat-mdc-form-field-subscript-wrapper { |
|||
display: none; |
|||
} |
|||
|
|||
input.mat-mdc-input-element { |
|||
font-size: 14px; |
|||
color: #3e4a59; |
|||
|
|||
&::placeholder { |
|||
color: #8b95a2; |
|||
opacity: 1; |
|||
} |
|||
} |
|||
|
|||
.mat-icon { |
|||
color: #8b95a2; |
|||
font-size: 20px; |
|||
width: 20px; |
|||
height: 20px; |
|||
} |
|||
|
|||
.mat-mdc-form-field-icon-prefix { |
|||
padding: 0 8px 0 0; |
|||
|
|||
> .mat-icon { |
|||
padding: 0; |
|||
} |
|||
} |
|||
|
|||
.mat-mdc-form-field-icon-suffix { |
|||
padding: 0; |
|||
|
|||
.mat-mdc-icon-button { |
|||
width: 32px; |
|||
height: 32px; |
|||
padding: 4px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.mat-focused ::ng-deep { |
|||
.mdc-notched-outline__leading, |
|||
.mdc-notched-outline__notch, |
|||
.mdc-notched-outline__trailing { |
|||
border-color: #8b95a2; |
|||
border-width: 1px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tb-iot-hub-installed-table-container { |
|||
border-radius: 4px; |
|||
overflow: hidden; |
|||
|
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
.mat-column-actions { |
|||
text-align: right; |
|||
white-space: nowrap; |
|||
} |
|||
} |
|||
|
|||
.tb-iot-hub-type-chip { |
|||
padding: 2px 10px; |
|||
border-radius: 12px; |
|||
font-size: 12px; |
|||
font-weight: 500; |
|||
white-space: nowrap; |
|||
|
|||
&.tb-type-widget { background: #e3f2fd; color: #1565c0; } |
|||
&.tb-type-dashboard { background: #e8f5e9; color: #2e7d32; } |
|||
&.tb-type-calc-field { background: #fff3e0; color: #e65100; } |
|||
&.tb-type-rule-chain { background: #f3e5f5; color: #7b1fa2; } |
|||
&.tb-type-device { background: #eceff1; color: #455a64; } |
|||
} |
|||
|
|||
.tb-iot-hub-installed-empty { |
|||
padding: 48px 24px; |
|||
text-align: center; |
|||
color: rgba(0, 0, 0, 0.54); |
|||
font-size: 14px; |
|||
} |
|||
@ -0,0 +1,210 @@ |
|||
///
|
|||
/// 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.
|
|||
///
|
|||
|
|||
import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { MatDialog } from '@angular/material/dialog'; |
|||
import { MatSort } from '@angular/material/sort'; |
|||
import { MatPaginator } from '@angular/material/paginator'; |
|||
import { IotHubApiService } from '@core/http/iot-hub-api.service'; |
|||
import { DialogService } from '@core/services/dialog.service'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { ActionNotificationShow } from '@core/notification/notification.actions'; |
|||
import { Subject } from 'rxjs'; |
|||
import { debounceTime } from 'rxjs/operators'; |
|||
import { PageLink } from '@shared/models/page/page-link'; |
|||
import { Direction, SortOrder } from '@shared/models/page/sort-order'; |
|||
import { IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models'; |
|||
import { ItemType, itemTypeTranslations } from '@shared/models/iot-hub/iot-hub-item.models'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
import { getEntityDetailsPageURL } from '@core/utils'; |
|||
import { TbIotHubItemDetailDialogComponent, IotHubItemDetailDialogData } from './iot-hub-item-detail-dialog.component'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-iot-hub-installed-items', |
|||
standalone: false, |
|||
templateUrl: './iot-hub-installed-items.component.html', |
|||
styleUrls: ['./iot-hub-installed-items.component.scss'] |
|||
}) |
|||
export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit { |
|||
|
|||
displayedColumns: string[] = ['itemName', 'itemType', 'version', 'createdTime', 'actions']; |
|||
dataSource: IotHubInstalledItem[] = []; |
|||
totalElements = 0; |
|||
pageSize = 10; |
|||
pageIndex = 0; |
|||
isLoading = false; |
|||
textSearch = ''; |
|||
|
|||
private searchSubject = new Subject<string>(); |
|||
|
|||
@ViewChild(MatSort, {static: true}) sort: MatSort; |
|||
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator; |
|||
|
|||
private static readonly ITEM_TYPE_TO_ENTITY_TYPE: Record<string, EntityType> = { |
|||
'WIDGET': EntityType.WIDGET_TYPE, |
|||
'DASHBOARD': EntityType.DASHBOARD, |
|||
'CALCULATED_FIELD': EntityType.CALCULATED_FIELD, |
|||
'RULE_CHAIN': EntityType.RULE_CHAIN, |
|||
'DEVICE': EntityType.DEVICE_PROFILE |
|||
}; |
|||
|
|||
constructor( |
|||
private iotHubApiService: IotHubApiService, |
|||
private dialogService: DialogService, |
|||
private translate: TranslateService, |
|||
private store: Store<AppState>, |
|||
private router: Router, |
|||
private dialog: MatDialog |
|||
) {} |
|||
|
|||
ngOnInit(): void { |
|||
this.searchSubject.pipe( |
|||
debounceTime(300) |
|||
).subscribe(() => { |
|||
this.pageIndex = 0; |
|||
this.paginator.pageIndex = 0; |
|||
this.loadData(); |
|||
}); |
|||
this.loadData(); |
|||
} |
|||
|
|||
ngAfterViewInit(): void { |
|||
this.sort.sortChange.subscribe(() => { |
|||
this.pageIndex = 0; |
|||
this.paginator.pageIndex = 0; |
|||
this.loadData(); |
|||
}); |
|||
} |
|||
|
|||
navigateToMarketplace(): void { |
|||
this.router.navigate(['/iot-hub']); |
|||
} |
|||
|
|||
onSearchChange(value: string): void { |
|||
this.textSearch = value; |
|||
this.searchSubject.next(value); |
|||
} |
|||
|
|||
onPageChange(event: any): void { |
|||
this.pageIndex = event.pageIndex; |
|||
this.pageSize = event.pageSize; |
|||
this.loadData(); |
|||
} |
|||
|
|||
deleteItem(item: IotHubInstalledItem): void { |
|||
this.dialogService.confirm( |
|||
this.translate.instant('iot-hub.delete-installed-item-title'), |
|||
this.translate.instant('iot-hub.delete-installed-item-text', {name: item.itemName}), |
|||
this.translate.instant('action.no'), |
|||
this.translate.instant('action.yes') |
|||
).subscribe(result => { |
|||
if (result) { |
|||
this.iotHubApiService.deleteInstalledItem(item.itemId).subscribe({ |
|||
next: () => { |
|||
this.store.dispatch(new ActionNotificationShow({ |
|||
message: this.translate.instant('iot-hub.installed-item-deleted', {name: item.itemName}), |
|||
type: 'success', |
|||
duration: 3000 |
|||
})); |
|||
this.loadData(); |
|||
}, |
|||
error: () => { |
|||
this.store.dispatch(new ActionNotificationShow({ |
|||
message: this.translate.instant('iot-hub.installed-item-delete-error', {name: item.itemName}), |
|||
type: 'error', |
|||
duration: 5000 |
|||
})); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
getItemTypeLabel(itemType: string): string { |
|||
const key = itemTypeTranslations.get(itemType as ItemType); |
|||
return key ? this.translate.instant(key) : itemType; |
|||
} |
|||
|
|||
getItemTypeChipClass(itemType: string): string { |
|||
switch (itemType) { |
|||
case 'WIDGET': return 'tb-type-widget'; |
|||
case 'DASHBOARD': return 'tb-type-dashboard'; |
|||
case 'CALCULATED_FIELD': return 'tb-type-calc-field'; |
|||
case 'RULE_CHAIN': return 'tb-type-rule-chain'; |
|||
case 'DEVICE': return 'tb-type-device'; |
|||
default: return ''; |
|||
} |
|||
} |
|||
|
|||
viewItemDetails(item: IotHubInstalledItem): void { |
|||
this.iotHubApiService.getVersionInfo(item.itemVersionId, {ignoreLoading: true}).subscribe(versionView => { |
|||
this.dialog.open(TbIotHubItemDetailDialogComponent, { |
|||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
|||
data: { |
|||
item: versionView, |
|||
iotHubApiService: this.iotHubApiService, |
|||
installedDescriptor: item.descriptor |
|||
} as IotHubItemDetailDialogData |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
getEntityId(item: IotHubInstalledItem): string | null { |
|||
const descriptor = item.descriptor; |
|||
switch (descriptor.type) { |
|||
case 'WIDGET': return descriptor.widgetTypeId?.id; |
|||
case 'DASHBOARD': return descriptor.dashboardId?.id; |
|||
case 'CALCULATED_FIELD': return descriptor.calculatedFieldId?.id; |
|||
case 'RULE_CHAIN': return descriptor.ruleChainId?.id; |
|||
default: return null; |
|||
} |
|||
} |
|||
|
|||
openEntity(item: IotHubInstalledItem): void { |
|||
const entityType = TbIotHubInstalledItemsComponent.ITEM_TYPE_TO_ENTITY_TYPE[item.itemType]; |
|||
const entityId = this.getEntityId(item); |
|||
if (entityType && entityId) { |
|||
const url = getEntityDetailsPageURL(entityId, entityType); |
|||
if (url) { |
|||
this.router.navigateByUrl(url); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private loadData(): void { |
|||
this.isLoading = true; |
|||
const sortOrder: SortOrder = { |
|||
property: this.sort?.active || 'createdTime', |
|||
direction: this.sort?.direction === 'asc' ? Direction.ASC : Direction.DESC |
|||
}; |
|||
const pageLink = new PageLink(this.pageSize, this.pageIndex, this.textSearch || null, sortOrder); |
|||
this.iotHubApiService.getInstalledItems(pageLink, {ignoreLoading: true}).subscribe({ |
|||
next: (data) => { |
|||
this.dataSource = data.data; |
|||
this.totalElements = data.totalElements; |
|||
this.isLoading = false; |
|||
}, |
|||
error: () => { |
|||
this.dataSource = []; |
|||
this.totalElements = 0; |
|||
this.isLoading = false; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
///
|
|||
/// 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.
|
|||
///
|
|||
|
|||
import { BaseData } from '@shared/models/base-data'; |
|||
|
|||
export interface WidgetInstalledItemDescriptor { |
|||
type: 'WIDGET'; |
|||
widgetTypeId: { id: string }; |
|||
} |
|||
|
|||
export interface DashboardInstalledItemDescriptor { |
|||
type: 'DASHBOARD'; |
|||
dashboardId: { id: string }; |
|||
} |
|||
|
|||
export interface CalculatedFieldInstalledItemDescriptor { |
|||
type: 'CALCULATED_FIELD'; |
|||
calculatedFieldId: { id: string }; |
|||
} |
|||
|
|||
export interface RuleChainInstalledItemDescriptor { |
|||
type: 'RULE_CHAIN'; |
|||
ruleChainId: { id: string }; |
|||
} |
|||
|
|||
export interface DeviceInstalledItemDescriptor { |
|||
type: 'DEVICE'; |
|||
} |
|||
|
|||
export type IotHubInstalledItemDescriptor = |
|||
| WidgetInstalledItemDescriptor |
|||
| DashboardInstalledItemDescriptor |
|||
| CalculatedFieldInstalledItemDescriptor |
|||
| RuleChainInstalledItemDescriptor |
|||
| DeviceInstalledItemDescriptor; |
|||
|
|||
export interface IotHubInstalledItemInfo { |
|||
itemId: string; |
|||
itemVersionId: string; |
|||
} |
|||
|
|||
export interface IotHubInstalledItem extends BaseData<{id: string}> { |
|||
tenantId: { id: string }; |
|||
itemId: string; |
|||
itemVersionId: string; |
|||
itemName: string; |
|||
itemType: string; |
|||
version: string; |
|||
descriptor: IotHubInstalledItemDescriptor; |
|||
} |
|||
Loading…
Reference in new issue