Browse Source

fix(iot-hub): fix dashboardId serialization in device register request

DashboardId implements EntityId which uses EntityIdDeserializer requiring
both entityType and id fields. Frontend was sending {"id":"..."} without
entityType, causing "Missing entityType or id!" 400 error.

Fix: include entityType: "DASHBOARD" in the dashboardId payload.
Add deserialization unit test to verify the exact frontend payload format.
pull/15347/head
Andrii Shvaika 4 months ago
parent
commit
5dadafdafc
  1. 75
      common/data/src/test/java/org/thingsboard/server/common/data/iot_hub/DeviceInstalledItemDescriptorTest.java
  2. 2
      ui-ngx/src/app/core/http/iot-hub-api.service.ts
  3. 4
      ui-ngx/src/app/modules/home/pages/iot-hub/device-install-dialog/device-install-dialog.component.ts

75
common/data/src/test/java/org/thingsboard/server/common/data/iot_hub/DeviceInstalledItemDescriptorTest.java

@ -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.common.data.iot_hub;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DashboardId;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
class DeviceInstalledItemDescriptorTest {
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void deserializeFromFrontendPayload() throws Exception {
// Exact payload the frontend sends after the fix
String json = """
{
"type": "DEVICE",
"createdEntityIds": [
{"entityType": "DEVICE_PROFILE", "id": "3eb9c330-2e57-11f1-9334-a385172e8e7d"},
{"entityType": "DEVICE", "id": "8a90d5e0-2e5d-11f1-a802-c35a9af2ebde"},
{"entityType": "DASHBOARD", "id": "8bc229f0-2e5d-11f1-a802-c35a9af2ebde"}
],
"dashboardId": {"entityType": "DASHBOARD", "id": "8bc229f0-2e5d-11f1-a802-c35a9af2ebde"}
}
""";
// Deserialize the same way the controller does (JsonNode → treeToValue)
JsonNode node = mapper.readTree(json);
DeviceInstalledItemDescriptor descriptor = mapper.treeToValue(node, DeviceInstalledItemDescriptor.class);
assertThat(descriptor).isNotNull();
assertThat(descriptor.getCreatedEntityIds()).hasSize(3);
assertThat(descriptor.getCreatedEntityIds().get(0).getEntityType()).isEqualTo(EntityType.DEVICE_PROFILE);
assertThat(descriptor.getCreatedEntityIds().get(1).getEntityType()).isEqualTo(EntityType.DEVICE);
assertThat(descriptor.getCreatedEntityIds().get(2).getEntityType()).isEqualTo(EntityType.DASHBOARD);
assertThat(descriptor.getDashboardId()).isNotNull();
assertThat(descriptor.getDashboardId().getId()).isEqualTo(UUID.fromString("8bc229f0-2e5d-11f1-a802-c35a9af2ebde"));
}
@Test
void roundTripSerialization() throws Exception {
DeviceInstalledItemDescriptor original = new DeviceInstalledItemDescriptor();
DashboardId dashboardId = new DashboardId(UUID.randomUUID());
original.setDashboardId(dashboardId);
original.setCreatedEntityIds(java.util.List.of(dashboardId));
String json = mapper.writeValueAsString(original);
JsonNode node = mapper.readTree(json);
DeviceInstalledItemDescriptor deserialized = mapper.treeToValue(node, DeviceInstalledItemDescriptor.class);
assertThat(deserialized.getDashboardId().getId()).isEqualTo(original.getDashboardId().getId());
assertThat(deserialized.getCreatedEntityIds()).hasSize(1);
assertThat(deserialized.getCreatedEntityIds().get(0).getId()).isEqualTo(dashboardId.getId());
}
}

2
ui-ngx/src/app/core/http/iot-hub-api.service.ts

@ -114,7 +114,7 @@ export class IotHubApiService {
public registerDeviceInstall(
versionId: string,
descriptor: { type?: string; createdEntityIds: { entityType: string; id: string }[]; dashboardId?: { id: string } },
descriptor: { type?: string; createdEntityIds: { entityType: string; id: string }[]; dashboardId?: { entityType: string; id: string } },
config?: IotHubRequestConfig
): Observable<InstallItemVersionResult> {
return this.http.post<InstallItemVersionResult>(

4
ui-ngx/src/app/modules/home/pages/iot-hub/device-install-dialog/device-install-dialog.component.ts

@ -498,12 +498,12 @@ export class TbDeviceInstallDialogComponent implements OnInit {
return ids;
}
private findCreatedDashboardId(): { id: string } | undefined {
private findCreatedDashboardId(): { entityType: string; id: string } | undefined {
for (const ws of this.wizardSteps) {
if (ws.type === 'progress' && ws.entitySteps) {
for (const ep of ws.entitySteps) {
if (ep.step.type === InstallStepType.DASHBOARD && ep.status === 'success' && ep.entityOutput) {
return { id: ep.entityOutput.id };
return { entityType: 'DASHBOARD', id: ep.entityOutput.id };
}
}
}

Loading…
Cancel
Save