Browse Source

avoid recursion to find relatedItems and change method names

pull/15682/head
dpinkevych 4 months ago
parent
commit
f6ed8b2fda
  1. 57
      application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java
  2. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html
  3. 8
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts

57
application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java

@ -683,39 +683,19 @@ public class DefaultIotHubService implements IotHubService {
Set<UUID> alreadyInstalledItemIds = new HashSet<>(iotHubInstalledItemService.findInstalledItemIdsByTenantId(tenantId));
// LinkedHashMap preserves insertion order; we add deps before the root so a single
// forward iteration yields the correct topological install sequence.
// LinkedHashMap preserves insertion order; we add the related items (deps) before the
// root so a single forward iteration yields the correct install sequence (deps first,
// root last). Related items are leaf dependencies — IoT Hub items are only ever one
// level deep, so there is no need to walk a related item's own related items.
LinkedHashMap<String, InstallPlanEntry> entries = new LinkedHashMap<>();
Set<String> visiting = new HashSet<>();
collectDependencies(rootVersion, alreadyInstalledItemIds, entries, visiting, true);
return new InstallPlan(versionId, new ArrayList<>(entries.values()));
}
/**
* Depth-first walk over {@code relatedItems}. Children are added BEFORE the current
* version so the resulting LinkedHashMap iterates deps-first, root-last.
*/
private void collectDependencies(JsonNode versionInfo,
Set<UUID> alreadyInstalledItemIds,
LinkedHashMap<String, InstallPlanEntry> entries,
Set<String> visiting,
boolean root) {
String itemId = versionInfo.get("itemId").asText();
if (entries.containsKey(itemId)) {
return;
}
if (!visiting.add(itemId)) {
log.warn("Dependency cycle detected involving IoT Hub item {} — breaking", itemId);
return;
}
JsonNode related = versionInfo.get("relatedItems");
String rootItemId = rootVersion.get("itemId").asText();
JsonNode related = rootVersion.get("relatedItems");
if (related != null && related.isArray()) {
for (JsonNode relatedNode : related) {
String relatedItemId = relatedNode.asText();
if (relatedItemId == null || relatedItemId.isEmpty() || entries.containsKey(relatedItemId)) {
if (relatedItemId == null || relatedItemId.isEmpty()
|| relatedItemId.equals(rootItemId) || entries.containsKey(relatedItemId)) {
continue;
}
JsonNode relatedVersion;
@ -731,11 +711,28 @@ public class DefaultIotHubService implements IotHubService {
entries.put(relatedItemId, missingEntry(relatedItemId, "Item not found or not published"));
continue;
}
collectDependencies(relatedVersion, alreadyInstalledItemIds, entries, visiting, false);
addPlanEntry(relatedVersion, alreadyInstalledItemIds, entries, false);
}
}
visiting.remove(itemId);
addPlanEntry(rootVersion, alreadyInstalledItemIds, entries, true);
return new InstallPlan(versionId, new ArrayList<>(entries.values()));
}
/**
* Build an {@link InstallPlanEntry} for a single marketplace version and append it to the
* plan. No traversal of {@code relatedItems} happens here — related items are resolved one
* level deep by {@link #resolveInstallPlan}.
*/
private void addPlanEntry(JsonNode versionInfo,
Set<UUID> alreadyInstalledItemIds,
LinkedHashMap<String, InstallPlanEntry> entries,
boolean root) {
String itemId = versionInfo.get("itemId").asText();
if (entries.containsKey(itemId)) {
return;
}
InstallPlanEntry entry = new InstallPlanEntry();
entry.setItemId(itemId);

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html

@ -197,7 +197,7 @@
<button mat-button (click)="cancelPlan()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary"
[disabled]="planSummary.willInstall === 0"
(click)="confirmPlan()">
(click)="installItemWithDependencies()">
{{ 'iot-hub.install-plan-confirm' | translate }}
</button>
}

8
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts

@ -259,9 +259,9 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
|| summary.alreadyInstalled > 0
|| summary.missing > 0;
if (!hasDependencies && summary.willInstall === 1) {
if (!hasDependencies) {
// Single root version, no deps, nothing to skip — install directly without showing the plan.
this.legacyInstall();
this.installItem();
return;
}
@ -277,7 +277,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
});
}
confirmPlan(): void {
installItemWithDependencies(): void {
if (!this.installPlan) {
return;
}
@ -288,7 +288,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
});
}
private legacyInstall(): void {
private installItem(): void {
this.state = 'installing';
const versionId = this.item.id as string;
this.iotHubApiService.installItemVersion(versionId, { ignoreLoading: true }, this.installData()).subscribe({

Loading…
Cancel
Save