Browse Source

feat(iot-hub): surface EntitiesLimitExceededException to caller

Re-throw EntitiesLimitExceededException from IoT Hub item install and
solution-template install paths instead of folding it into the generic
install error response, so the PE entity-limit error reaches the global
HTTP error handler.

In the device install wizard switch saveDevice/createDevice (and
overwrite) calls from ignoreErrors:true to false so the limit-exceeded
toast is shown when the backend rejects the create.
pull/15548/head
Igor Kulikov 5 months ago
parent
commit
8b016df3bf
  1. 4
      application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java
  3. 8
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts

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

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.iot_hub.IotHubInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.RuleChainInstalledItemDescriptor; import org.thingsboard.server.common.data.iot_hub.RuleChainInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.SolutionTemplateInstalledItemDescriptor; import org.thingsboard.server.common.data.iot_hub.SolutionTemplateInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.WidgetInstalledItemDescriptor; import org.thingsboard.server.common.data.iot_hub.WidgetInstalledItemDescriptor;
import org.thingsboard.server.exception.EntitiesLimitExceededException;
import org.thingsboard.server.service.solutions.SolutionService; import org.thingsboard.server.service.solutions.SolutionService;
import org.thingsboard.server.service.solutions.data.solution.SolutionInstallResponse; import org.thingsboard.server.service.solutions.data.solution.SolutionInstallResponse;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
@ -135,6 +136,9 @@ public class DefaultIotHubService implements IotHubService {
return InstallItemVersionResult.success(descriptor); return InstallItemVersionResult.success(descriptor);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to install IoT Hub item version: {}", tenantId, versionId, e); log.error("[{}] Failed to install IoT Hub item version: {}", tenantId, versionId, e);
if (e instanceof EntitiesLimitExceededException el) {
throw el;
}
return InstallItemVersionResult.error(e.getMessage()); return InstallItemVersionResult.error(e.getMessage());
} }
} }

4
application/src/main/java/org/thingsboard/server/service/solutions/DefaultSolutionService.java

@ -93,6 +93,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.exception.EntitiesLimitExceededException;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
@ -409,6 +410,9 @@ public class DefaultSolutionService implements SolutionService {
} catch (Throwable e) { } catch (Throwable e) {
log.error("[{}][{}] Failed to install solution template", tenantId, solutionId, e); log.error("[{}][{}] Failed to install solution template", tenantId, solutionId, e);
rollback(tenantId, solutionId, ctx, e); rollback(tenantId, solutionId, ctx, e);
if (e instanceof EntitiesLimitExceededException el) {
throw el;
}
return new SolutionInstallResponse( return new SolutionInstallResponse(
new TenantSolutionTemplateInstructions(ctx.getSolutionInstructions()), new TenantSolutionTemplateInstructions(ctx.getSolutionInstructions()),
false, false,

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

@ -748,12 +748,12 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
return { id: result.id.id, name: result.name, url: `/profiles/deviceProfiles/${result.id.id}` }; return { id: result.id.id, name: result.name, url: `/profiles/deviceProfiles/${result.id.id}` };
} }
case InstallStepType.DEVICE: { case InstallStepType.DEVICE: {
const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: true})); const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: false}));
const creds = await this.resolveCredentials(step, result.id.id); const creds = await this.resolveCredentials(step, result.id.id);
return { id: result.id.id, name: result.name, url: `/entities/devices/${result.id.id}`, token: creds.credentialsId }; return { id: result.id.id, name: result.name, url: `/entities/devices/${result.id.id}`, token: creds.credentialsId };
} }
case InstallStepType.GATEWAY: { case InstallStepType.GATEWAY: {
const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: true})); const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: false}));
const creds = await this.resolveCredentials(step, result.id.id); const creds = await this.resolveCredentials(step, result.id.id);
const output = { const output = {
id: result.id.id, id: result.id.id,
@ -904,13 +904,13 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
} }
case InstallStepType.DEVICE: { case InstallStepType.DEVICE: {
template.id = { id: existing.id, entityType: 'DEVICE' }; template.id = { id: existing.id, entityType: 'DEVICE' };
const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: true})); const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: false}));
const creds = await this.resolveCredentials(step, result.id.id); const creds = await this.resolveCredentials(step, result.id.id);
return { id: result.id.id, name: result.name, url: `/entities/devices/${result.id.id}`, token: creds.credentialsId }; return { id: result.id.id, name: result.name, url: `/entities/devices/${result.id.id}`, token: creds.credentialsId };
} }
case InstallStepType.GATEWAY: { case InstallStepType.GATEWAY: {
template.id = { id: existing.id, entityType: 'DEVICE' }; template.id = { id: existing.id, entityType: 'DEVICE' };
const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: true})); const result = await firstValueFrom(this.deviceService.saveDevice(template, {ignoreErrors: false}));
const creds = await this.resolveCredentials(step, result.id.id); const creds = await this.resolveCredentials(step, result.id.id);
const output: EntityStepOutput = { const output: EntityStepOutput = {
id: result.id.id, id: result.id.id,

Loading…
Cancel
Save