Root entries other than WIDGET or SOLUTION_TEMPLATE should always be installed even when a prior version is already installed — only widgets and solution templates are deduplicated by item id at install-plan resolution time.
Switch from base64 data URLs to URL.createObjectURL for images extracted from the device package zip. Revokes them in ngOnDestroy to avoid leaking memory, and avoids embedding multi-megabyte data URLs in the DOM.
Split the detail dialog close button into desktop (inline in header) and mobile (absolute, top-right) variants. Switch meta-grid first column to minmax(0, …) so the creator section can shrink, and truncate the creator name with a tooltip. Add 8px right margin to the card type label.
Move the zip fetch from IotHubActionsService into TbDeviceInstallDialogComponent.ngOnInit so the dialog opens immediately and shows its own loading spinner instead of blocking the caller. Drops the now-unused fetchZipData helper and zipData field from DeviceInstallDialogData.
Drop the (optionSelected) handler and route navigation through the option's (click) instead. Setting [value]="searchText" makes Material write the typed string back through ngModel (a no-op) rather than the MpItemVersionView object, so the input keeps the user's query after the detail dialog closes and searchText.trim() in seeAllResults() stays safe.
Route all parse-failure throws through a single parseFailure(action, itemType[, section], cause) helper so the user-facing wording lives in one place and update paths share the template with install paths.
- Inject IotHubActionsService into AlarmRulesTableConfig (and the
surrounding alarm-rules table component + AlarmRulesTableConfig
resolver) and register a third header action descriptor — "Add
from IoT Hub" (icon: hub) — that calls
iotHubActions.addItem(ItemType.ALARM_RULE, { entityId }) and
refreshes the table on a successful install.
- Extend TbIotHubAddItemDialogComponent so the same
TbIotHubSelectCfEntityDialogComponent prompt is reused when an
ALARM_RULE install is launched without a preselected entityId,
not just for CALCULATED_FIELD installs.
Align the four entity table "Add from IoT Hub" descriptors (devices,
rule chains, calculated fields, widget types) with the new hub icon
already used by the IoT Hub menu and breadcrumb. Drops the old
`store` glyph that no longer matches the rest of the IoT Hub UI.
- IotHubRestClient.getVersionFileData now streams the response and
enforces a configurable size cap (iot-hub.max-file-data-size-bytes,
env IOT_HUB_MAX_FILE_DATA_SIZE_BYTES, default 100 MiB). Oversized
payloads are rejected up front via Content-Length and mid-stream
for chunked transfers, preventing OOMs from unknown bodies.
- DefaultIotHubService guards every iotHubRestClient.getVersionInfo
call against a null response, and rejects update attempts where
the installed item's type does not match the new version's type
("Installed item type does not match the new version's item type.").
Solution-template descriptor is also populated with
tenantTelemetryKeys / tenantAttributeKeys captured during install.
Drops the empty updateDeviceProfile stub.
- Add a tenant-scoped findByTenantIdAndId + deleteByTenantIdAndId to
IotHubInstalledItemDao (and the bulk deleteByTenantIdAndIdIn JPA
query). IotHubInstalledItemServiceImpl now uses these so reads
and deletes always honour the tenant boundary.
- Database: add lts schema_update.sql + schema-entities-idx.sql
indexes on iot_hub_installed_item(tenant_id),
(tenant_id, item_type), and (tenant_id, item_id) for the new
tenant-scoped lookups.
- DefaultSolutionService: minor cleanup touched alongside.
- .gitignore trailing-newline fix; remove stale superpowers
plan/spec docs that have been folded into the implementation.