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.
- New input[matAutocomplete]/textarea[matAutocomplete] directive
that keeps an open MatAutocomplete panel attached to its anchor
while the page scrolls or the window resizes: tracks the
input's visibility via IntersectionObserver and re-requests
panel position (or closes the panel when off-screen) when the
nearest scroll container or window emits a resize/scroll event.
- Declare and export the directive from SharedModule so it lights
up across every screen that uses mat-autocomplete.
- Drop the transparent backdrop from
MAT_AUTOCOMPLETE_DEFAULT_OPTIONS (the directive now handles
scroll-and-resize repositioning, so the backdrop-driven outside-
click close is no longer needed) and keep the existing
hideSingleSelectionIndicator default.
Add a DEVICE arm to getInstalledItemUrl so device-installs link to
their generated dashboard (descriptor.dashboardId / EntityType.DASHBOARD),
matching the existing SOLUTION_TEMPLATE handling instead of falling
through to no URL.
Bump the installed-items table .mat-column-createdTime width
from 100px to 160px so the new "MMM d, y, h:mm a" format
(introduced when the column started showing the install time)
fits without wrapping or ellipsis.
Switch the createdTime column formatter from 'mediumDate' (date
only) to the custom pattern 'MMM d, y, h:mm a' so each row also
surfaces the install time. Angular's built-in 'medium' alias was
not used because it includes seconds.
- Guard the helper against a missing descriptor by short-circuiting
to null up front instead of letting the switch crash on
`undefined.type`.
- Expand the inline single-line `case 'X': … break;` arms in the
switch so each branch reads on its own line, matching the
formatting of the already-multi-line CALCULATED_FIELD /
ALARM_RULE arms.
- Add a single getInstalledItemUrl(descriptor) helper to
iot-hub-installed-item.models that maps every descriptor type
(WIDGET / DASHBOARD / CALCULATED_FIELD / ALARM_RULE / RULE_CHAIN /
SOLUTION_TEMPLATE) to its entity-details URL via the shared
getEntityDetailsPageURL utility, appending `?selectedTab=cf` for
calculated-field installs so the entity tabs page lands on the CF
tab on first paint.
- Drop the parallel resolveEntityDetailsUrl helper +
iot-hub-components.models.ts file and rewire every installed-item
open path (install dialog, update dialog, installed-items table,
item detail dialog) to call getInstalledItemUrl. Installed-items
table openEntity collapses to parseUrl + serializeUrl on the
helper's output now that any selectedTab is part of the URL.
- Teach EntityDetailsPageComponent to honour a `selectedTab=…`
query param: read it from the current route on entityId change,
strip it (replaceUrl) so refresh doesn't keep jumping the tab,
then resolve a tab index via the new
EntityTabsComponent.resolveTabIndex(tab) hook. Asset, device,
asset-profile, and device-profile tabs override the hook to map
the `cf` shortcut to their respective Calculated Fields tab
position; other entities fall back to index 0.
- EntityDetailsPanelComponent now applies this.selectedTab to the
underlying MatTabGroup both when the entity-tabs query list
refreshes and after view init, so the initial tab matches the
resolved index instead of staying on 0.
- Add a `showOverflowedTitle` boolean input (defaults to false,
coerceBoolean-decorated) to ChipOverflowDirective. When true and
hiddenCount > 0, the overflow `+N` chip gets a `title` attribute
set to the comma-joined trimmed textContent of every overflowing
chip, surfacing them via the native browser tooltip. When the
flag is off, no items overflow, or hiddenCount drops back to
zero, the title attribute is removed.
- Keep `pointer-events` in sync with the title so the native
tooltip actually fires: remove `pointer-events: none` while a
title is present; reapply it otherwise (matches the initial
state set in createOverflowChip).
- Opt the IoT Hub item detail dialog's three tb-chip-overflow
meta-chip groups (connectivity, categories, use-cases) into
showOverflowedTitle so hovering the `+N` chip lists the hidden
values.
- TbIotHubMarkdownComponent.parseData now runs through a
forceLinksOpenInNewTab post-pass that (a) appends the
{:target="_blank"} suffix to the TEXT of any markdown link
[text](url) that doesn't already carry it, matching the
MarkedOptionsService.renderer.link contract that strips the
suffix and rewrites the <a> with target="_blank", and (b)
injects target="_blank" into raw <a ...> HTML anchors that
don't already declare a target= attribute. Image syntax
 is excluded via lookbehind.
- iot-hub-markdown SCSS narrows the link styling selector to
a:not(.mdc-button):not(.tb-iot-hub-item-link-card) so the
embedded item-link card anchor keeps its own typography.
- iot-hub-item-link-card now renders a verified icon (teal
#00695c via a new tb-iot-hub-item-link-verified-icon class)
when item.creatorVerified is true, matching the item-card
treatment; the default person icon is used otherwise.
- Add itemTypeIcons + getItemTypeIcon helper to
iot-hub-item.models, mirroring mp-item.models in the iot-hub
project: widgets / dashboard / apps / mdi:function-variant /
settings_ethernet / mdi:bell-cog / devices_other with `category`
as the fallback.
- Replace every hand-rolled item-type → icon switch with calls to
getItemTypeIcon: item detail dialog (getTypeIcon / getCompactIcon
fallback path), installed-items table (getItemTypeIcon),
iot-hub-home (getCompactIcon / getItemTypeIcon), item-card
(getPlaceholderIcon default + per-type fallbacks), item-link-card
(getCompactIcon / getTypeIcon).
- Swap the remaining mat-icon placeholders that render an item-type
icon to tb-icon (installed-items table chip, home search popup
thumb placeholder, item-link-card thumb fallback). Generic
status / action icons stay on mat-icon.
- Refresh the colored item-type chip palette in the installed
items table to match itemTypeChipColors from mp-item.models, and
add the previously-missing tb-type-alarm-rule rule so that chip
picks up the new orange palette instead of falling through to no
background.
Treat DEVICE, CALCULATED_FIELD, ALARM_RULE, and RULE_CHAIN as
count-based item types in maybeOpenDeepLinkedItem: all four resolve
via getInstalledItemCounts(this.config.type) and pass only { count }
to openItemDetail. Singular-entity types (widgets, dashboards,
solution templates) still go through resolveInstalledItem and pass
{ installed }.
- Extract the tenant-profile fetch lock stripe count to a named constant (PR #15744)
- Trim the stale global-lock sentence from the per-tenant lock comment (PR #15744)
- Rename the rate-limit onCreate callback to onMiss and document its idempotency requirement (PR #15744)
- Reuse a single tenant profile local in update(TenantProfileUpdateResult) (PR #15744)
- Make the transport callback thread pool size configurable via transport.callback_thread_pool_size (PR #15744)
- Add a parameterized test locking the TransportLimitsType enum-to-profile-field mapping (PR #15744)
- Add device/gateway rate-limit coverage asserting update(tenantId) reaches tracked entities (PR #15744)
- Add a same-tenant fetch-dedup test and pin the cross-tenant test to distinct stripes (PR #15744)
Construct CoapServer and the LwM2M bootstrap server inside the init try block so a failure in the constructor or build() is cleaned up by the existing catch. Guard CoAP shutdown() against a null server. Add a DTLS-enabled CoAP test that covers the dtlsSessionsExecutor shutdown branch.
The defective code lives in common/transport/transport-api and is shared by all
transports (MQTT, HTTP, CoAP, LwM2M, SNMP); the production incident happened to
surface on MQTT.
On a cold tenant-profile cache (e.g. after a cache clear + restart), a device
reconnect storm could serialize the whole transport instance behind tenant-profile
resolution, saturating the callback pool and stalling the node for ~15 minutes.
Two compounding causes are addressed:
- DefaultTransportTenantProfileCache held a single process-wide ReentrantLock across
the synchronous cross-service getEntityProfile round-trip, so every tenant-profile
cache miss in the whole process was serialized one-at-a-time. Replace it with a
bounded set of per-tenant locks (Guava Striped) so different tenants resolve
concurrently while concurrent misses for the same tenant are still de-duplicated.
- DefaultTransportRateLimitService performed that blocking fetch inside
ConcurrentHashMap.computeIfAbsent's mapping function, holding a CHM bin lock across
the remote round-trip. Pre-fetch the tenant profile before computeIfAbsent so no bin
lock is held across I/O.
Also de-duplicate the four near-identical getXRateLimits methods into one generic
helper, move the per-type rate-limit getters onto the TransportLimitsType enum, and
avoid fetching the tenant profile four times in update(TenantId).
Bump LangChain4j to 1.16.1-TB1, which adds frequencyPenalty and
presencePenalty support to the langchain4j-google-genai integration,
and pass both parameters through for Google Gemini and Google Vertex AI
Gemini models.
Collapse maybeOpenDeepLinkedItem into a single openItemDetail call.
Devices resolve via getInstalledItemCounts and pass only the
{ count } for the deep-linked itemId; every other type resolves via
resolveInstalledItem and passes only the matching { installed }
entity (no more synthetic 0/1 count for non-device types).
Bring the same fetch-failure UI to every IoT Hub surface (browse,
search, home, creator profile) so all of them recover gracefully
from network / server outages instead of dead-ending on a global
error toast or a hard redirect.
- Add a tb-no-service-bg global utility in form.scss that mirrors
tb-no-data-bg but renders /assets/home/no-service.svg (copied
from thingsboard.io). Same primary-color mask treatment, so PE
builds retint without re-exporting the asset.
- Add hasError / retryTimer state to the four affected components
and switch their data calls to ignoreErrors: true so the global
toast no longer fires. hasError flips to true on error, stays
true while retries are pending, and is only cleared in the
`next` callback of the next request that actually succeeds.
- Add retryLoad* helpers (retryLoadItems / retryLoadResults /
retryLoadPopularItems / retryLoadCreator) that set isLoading=true
immediately and schedule the real load via a 350ms setTimeout so
rapid-fire retry clicks coalesce into one network round-trip.
- Search component drops the inline switchMap-on-searchSubject path
and pipes the debounced subject straight into loadResults() so
search-triggered loads go through the same error / spinner /
retry plumbing.
- Render the error block consistently across surfaces: in browse
and search inside their respective results container, on the
home page right after the category cards, on the creator profile
as the whole page body (replacing the old router.navigate to
/iot-hub). Each block uses .tb-no-service-bg + the new
iot-hub.network-server-unavailable / -text / try-again locale
keys and binds the primary button to the matching retryLoad*
method.
- Add the matching .tb-iot-hub-empty-state typography (18/500
title, 14/0.54 body, 24px button gap) to the home and creator
profile SCSS so the wrapper matches the layout already used in
browse / search.
Also pick up an unrelated dashboard-widget-select adjustment touched
in the same workspace.
- Drop the DEVICE-only `tb-iot-hub-card-preview-device` /
`dlg-preview-device` variants in the item card and item detail
dialog. The previously-special white-background-with-border treatment
is gone — device previews now reuse the same dot-pattern background
as widgets / solution templates for a consistent look.
- In the home-page iot-hub widget, fold the
`tb-iot-hub-widget-card-image-pattern` modifier into the base
`.tb-iot-hub-widget-card-image` so every preview card already paints
the dot pattern (no opt-in class required).
- Rename `.tb-search-categories` to `.tb-results-container` across
the iot-hub-search component / search page / creator profile pages
so the wrapper class better reflects what it actually contains
(browse / search results) and stays consistent between the search
component and the screens that embed it.
- Pick up incidental browse / search component html / scss tweaks
touched alongside the rename.